#Introduction This analysis was done by Mike Goldweber, Sept 2023. Submitted to DrivenData.org on Oct 6, 2023. This document shows; the step by step process of analyzing the emergency room data provided in the Unsupervised Wisdom contest.

The full set of project files can be found at: https://github.com/halciber/Machine-Learning/tree/master/UnsupervisedWisdom.

Data Ingestion

The data used in this project is the Primary data set. This contains 115,128 rows of data. The data is pulled into two dataframes. One called df_original, and the other is called df_mapped. The df_mapped set is modified by the example code so that it produces human readable data. the df_original set will be modified by the feature engineer to be usable by the modeling done below.

#You'll want to adjust your file path for your environment
filepath <- "c:\\_working\\Machine-Learning\\UnsupervisedWisdom\\Data\\primary_data.csv"

df_original <- read.csv(filepath)

Setting up the categorical mapping dataframe

The code for the next two blocks was take directly from the Community Code section of the contest website, found at: https://www.drivendata.org/competitions/217/cdc-fall-narratives/community-code/.The purpose of this block is to take the JSON coding information and match it to the numeric values found in primary_data.csv. I’ve found this version of the dataset to be helpful in understanding the data. For example, “57 - FRACTURE” is more meaningful than the original “57”. My intention is to use the df_mapped dataframe primarily with the data exploration and the. df_original, created in the code block above will be used for the modeling work after modification in the Feature Engineer section below.

library(jsonlite)

mappingfile <- 'c:\\_working\\Machine-Learning\\UnsupervisedWisdom\\Data\\variable_mapping.json'
mapping <- fromJSON(mappingfile)
names(mapping)
 [1] "sex"              "race"             "hispanic"         "alcohol"          "drug"             "body_part"        "body_part_2"     
 [8] "diagnosis"        "diagnosis_2"      "disposition"      "location"         "fire_involvement" "product_1"        "product_2"       
[15] "product_3"       
# Convert to data frames so we can use in joins
mapping_tables <- list()
for (col in names(mapping)) {
    mapping_tables[[col]] <- data.frame(
        ind=as.integer(names(mapping[[col]])),  # change to integer types
        values=unlist(mapping[[col]])
    )
}

Now that the JSON information has been ingested, we’ll map the categories on to the dataframe, which we will call df_mapped.

library(dplyr)

# Load primary data
df_mapped <- df_primary

# Join and replace encoded column
for (col in names(mapping)) {
    df_mapped <- df_mapped %>%
        left_join(mapping_tables[[col]], by=setNames("ind", col)) %>%
        mutate(!!col := values) %>%
        select(-values)
}

Data Clean Up/ Preliminary Feature Engineer

Let’s look at the dataset to get an initial feel for the data quality by running a summary

summary(df_original)
 cpsc_case_number     narrative         treatment_date          age              sex             race         other_race           hispanic       diagnosis    
 Min.   :190103269   Length:115128      Length:115128      Min.   : 65.00   Min.   :1.000   Min.   :0.0000   Length:115128      Min.   :0.000   Min.   :42.00  
 1st Qu.:200255706   Class :character   Class :character   1st Qu.: 72.00   1st Qu.:1.000   1st Qu.:0.0000   Class :character   1st Qu.:0.000   1st Qu.:57.00  
 Median :210527801   Mode  :character   Mode  :character   Median : 79.00   Median :2.000   Median :1.0000   Mode  :character   Median :2.000   Median :57.00  
 Mean   :208188741                                         Mean   : 79.35   Mean   :1.631   Mean   :0.7957                      Mean   :1.259   Mean   :58.69  
 3rd Qu.:220460723                                         3rd Qu.: 86.00   3rd Qu.:2.000   3rd Qu.:1.0000                      3rd Qu.:2.000   3rd Qu.:62.00  
 Max.   :230222638                                         Max.   :112.00   Max.   :2.000   Max.   :6.0000                      Max.   :2.000   Max.   :74.00  
                                                                                                                                                               
 other_diagnosis     diagnosis_2    other_diagnosis_2    body_part      body_part_2     disposition       location     fire_involvement       alcohol       
 Length:115128      Min.   :41.0    Length:115128      Min.   : 0.00   Min.   : 0.00   Min.   :1.000   Min.   :0.000   Min.   :0.0000000   Min.   :0.00000  
 Class :character   1st Qu.:53.0    Class :character   1st Qu.:37.00   1st Qu.:35.00   1st Qu.:1.000   1st Qu.:1.000   1st Qu.:0.0000000   1st Qu.:0.00000  
 Mode  :character   Median :59.0    Mode  :character   Median :75.00   Median :75.00   Median :1.000   Median :1.000   Median :0.0000000   Median :0.00000  
                    Mean   :59.8                       Mean   :65.51   Mean   :63.98   Mean   :2.122   Mean   :1.715   Mean   :0.0005993   Mean   :0.02248  
                    3rd Qu.:63.0                       3rd Qu.:79.00   3rd Qu.:79.00   3rd Qu.:4.000   3rd Qu.:1.000   3rd Qu.:0.0000000   3rd Qu.:0.00000  
                    Max.   :74.0                       Max.   :94.00   Max.   :94.00   Max.   :6.000   Max.   :9.000   Max.   :3.0000000   Max.   :1.00000  
                    NA's   :71983                                      NA's   :71983                                                                        
      drug           product_1      product_2        product_3      
 Min.   :0.00000   Min.   : 110   Min.   :   0.0   Min.   :   0.00  
 1st Qu.:0.00000   1st Qu.:1715   1st Qu.:   0.0   1st Qu.:   0.00  
 Median :0.00000   Median :1807   Median :   0.0   Median :   0.00  
 Mean   :0.04035   Mean   :2168   Mean   : 504.1   Mean   :  56.16  
 3rd Qu.:0.00000   3rd Qu.:3299   3rd Qu.: 474.8   3rd Qu.:   0.00  
 Max.   :1.00000   Max.   :5043   Max.   :5040.0   Max.   :5040.00  
                                                                    

Usually a concern is missing data scattered randomly thoughtout the set. In this case, the summary shows the data is in good shape. That is there isn’t much in the way of missing data. The only NA’s we see are in the diagnosis_2 and body_part_2 columns. This isn’t a suprise, because the ER patients didn’t necessarily suffer secondary injuries. There is a correlation between these two columns, given the identical number of NAs at 71,983. These columns will have to be explored further to determine if it should be used in our modeling.

However, glancing at the data sets directly shows gaps in some of the other columns. For example body_part_2, other_diagnosis and other_diagnosis_2 contain many gaps.

head(df_mapped)

Data Exploration

This is actually the critical portion of the project, and most of the effort was spent on this work in order to understand the scope of the problem. The results of this exploration affected later portions of the exploration. Please note, the narrative column won’t be looked at in the data exploration. The goal is to use ChatGPT to analyze this data and return helpful results. So, this part of the data is being handled separately.

Let’s begin by looking at correlations between the columns.

Correlation Plot

library(corrplot)

#we need to use numerical values for the correlation. So, we'll make a subset of the data.
df_numsubset<- df_original[, c(4:6, 8:9, 13, 15:22)]

#visualization matrix of the data, looking for correlations
corrplot(cor(df_numsubset), type= "upper")

We see strong correlations between race and hispanic, product_1 and product_2, as well as product_2 and product_3. Honestly, this doesn’t seem to be very helpful. At least as this stage. Our focus is on the injuries. There are some connections to age and some of the other factors. Including alcohol. So, we’ll have to explore this.

Age

Let’s look at the age category. In particular, let’s see if there is a particular age that is hit harder than another age. The block converts it into a table, and the plot shows the frequency of injuries by age.

library(ggplot2)


#frequency of injuries by age
data <- df_numsubset[, c('age', 'sex')]
df_agefreq <- as.data.frame(table(data$age))
colnames(df_agefreq)[1] <- "age" 
colnames(df_agefreq)[2] <- "frequency" 
#head(df_agefreq)
ggplot(df_agefreq) + 
    geom_count(mapping = aes(x=age, y=frequency)) + 
    labs(title = "Frequency of Injury By Age", x="Age", y="Count")

What is interesting about this plot, it shows that the injury frequency is relatively high (over 3500) until age 88. I am wondering if the fall off is due to some environmental movement, or is the population over 88 shrinking? There is a bit of a plateau for ages 71-80, where each group has over 4000 injuries, except for age 76 with 3993 injuries.

Sex (Gender)

Next, let’s look at the breakdown of sex (gender) in this dataset.

#63% of the injuries are to women

genderlabels <- c("Male", "Female")
gender <- as.vector(df_original[ ,'sex'])
gentable <- as.data.frame(table(gender))

gentable$gender <- mapvalues(gentable$gender, c( "1", "2"), genderlabels)

pie(gentable$Freq, labels = genderlabels, main="Sex Breakdown")


percentoffemales <- (gentable[2, 'Freq']/nrow(df_original)*100)
out <- sprintf("Percentage of females in this dataset: %f)", percentoffemales) #percentage of females in this dataset
out
[1] "Percentage of females in this dataset: 63.115836)"

This plot visually shows the breakdown of sex in this dataset. We see that at 63.1%, females represents the majority of cases in this dataset. So, we’ll pay particular attention to the factors affecting this part of the population.

Age and Gender

Next, let’s look at the split by age and gender.

library(ggplot2)
library(sqldf)
library(reshape2)

results <- sqldf('SELECT age, sex, count(sex) AS "frequency"
                        FROM dfmapping
                        GROUP BY age, sex')

# reshape the dataframe into a long format
results <- melt (results, id.vars = c ("sex", "age"))

# plot two lines for different genders
ggplot (results, aes (x = age, y = value, group = sex, color = sex)) +
  geom_line () +
  labs (x = "Age", y = "Injury Frequency", color = "Sex")

This chart shows a significant difference by gender across the ages of the patients. First, this plot confirms the previous pie chart plot by showing the female population suffers greater numbers of injuries across the age spectrum. As we explore further, we’ll have to consider the possibility of using different approaches for helping each gender. Of note, this shows the data set only includes males and females. The other categories are not represented in this dataset.

Data Exploration by Race and Sex

Next, let’s factor in the race of the patients to see if any particular group is affected more than the others. After looking at the upper level data by race, we’ll look at the hispanic breakdown at the population.

Breakdown By Race

We’ll start off with a simple pie chart to see.


if (system.file(package = "waffle") == "") {
  install.packages("waffle")
}

library(ggplot2)
library(waffle)

rcounts <- as.data.frame(table(df_mapped$race))
colnames(rcounts)[1]<-"race"
colnames(rcounts)[2]<-"frequency"

vec <- numeric()
vecS <- character()

for(i in rcounts$frequency)
{
    x <- i/rowcount
    x <- x*100
    vec <- c(vec, x)
    
    s <- ifelse((x > 7.0), sprintf("%.2f%%", x), "")#display only meaning values on the pie chart
    vecS <- c(vecS, s)
}

rcounts <- cbind(rcounts, new_col = vec)
colnames(rcounts)[3]<-"percent"

rcounts <- cbind(rcounts, new_col = vecS)
colnames(rcounts)[4]<-"labels"

ggplot(rcounts, aes(x = "", y = percent, fill = race)) +
  geom_bar(stat="identity", width=1) +
  geom_text(aes(label = labels), position = position_stack(vjust = 0.5)) +
  scale_fill_manual(values = c("#E59866", "#FCF3CF", "#AF601A", "#AED6F1", "#BD0026", "#27AE60", "#FAD7AD")) + ##E59866
  coord_polar("y", start=0) + 
  labs(title = "Breakdown of the Patient Population by Race",
          #subtitle = "test",
          caption = "34.4% did not state their race, making a racial correlation difficult")

This chart is problematic, because of the high percentage of N.S. (not stated). So it maybe difficult to accurately identify a racial component to these injuries.

Hispanic

if (system.file(package = "waffle") == "") {
  install.packages("waffle")
}

library(ggplot2)
library(waffle)

hcounts <- as.data.frame(table(df_mapped$hispanic))
colnames(hcounts)[1]<-"hispanic"
colnames(hcounts)[2]<-"frequency"

vec <- numeric()
vecS <- character()

for(i in hcounts$frequency)
{
    x <- i/rowcount
    x <- x*100
    vec <- c(vec, x)
    
    s <- sprintf("%.2f%%", x)
    vecS <- c(vecS, s)
}

hcounts <- cbind(hcounts, new_col = vec)
colnames(hcounts)[3]<-"percent"

hcounts <- cbind(hcounts, new_col = vecS)
colnames(hcounts)[4]<-"labels"

ggplot(hcounts, aes(x = "", y = percent, fill = hispanic)) +
  geom_bar(stat="identity", width=3) +
  geom_text(aes(label = labels), position = position_stack(vjust = 0.5)) +
  scale_fill_manual(values = c("#E59866","#27AE60", "#FCF3CF")) +   
  coord_polar("y", start=0) + 
  labs(title = "Breakdown of the Hispanic Population",
          #subtitle = "test",
          caption = "Only 3% of the patients identify as Hispanic")

So, the majority of patients are not hispanic, however, we can’t conclude not being hispanic makes a person more likey to fall.

rhcounts <- as.data.frame(table(df_mapped$hispanic, df_mapped$race))
colnames(rhcounts)[1]<-"hispanic"
colnames(rhcounts)[2]<-"race"
colnames(rhcounts)[3]<-"frequency"

webr::PieDonut(rhcounts, aes(hispanic, race, count=frequency), 
              r0 = 0.3, #hides the center
               r1 = 0.7,
               #explode = 3,
               #explodeDonut = TRUE,
               pieLabelSize = 3,
               donutLabelSize = 4,
               title = "Hispanic and Racial Breakdown")
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.

As with the Hispanic pie chart, and with only 3% of the injured set being hispanic, I’m not convinced that hispanic=yes correlates to a fall. Further, no one racial group stands out Certainly, part of the Unk/Not Stated group is probably hispanic, but we don’t know for certain. Further obfuscating this part of the hispanic set is the N.S part of the race data, which represents 76% of the Unknown hispanic group.

We’ll continue to look at race, but I don’t believe there is anything to be gained from including the hispanic column in our model.

rm(rhcounts)
Warning: object 'rhcounts' not found

Diagnosis

Next is a look at the diagnosis column. We’ll start off by looking for the most heavily diagnosed injuries.

#grouping the diagnosis column, looking for the most frequent injury

##frequency of each diagnosis
dcounts <-df_mapped[, c('diagnosis')]

df_diagnosisfreq <- as.data.frame(table(dcounts))
colnames(df_diagnosisfreq)[1] <- "diagnosis_code" 
colnames(df_diagnosisfreq)[2] <- "frequency" 

#sort the data in descending order
df_diagnosisfreqSorted <- df_diagnosisfreq[order(-df_diagnosisfreq$frequency),]
head(df_diagnosisfreqSorted)

#cleanup
rm(dcounts, df_diagnosisfreq)

From this sorting, we know that fractures, internal injuries, contusions abj, and lacertations make up the majority of the 26 listed injuries by a significant amount.

Seeing the majority (86%) of the dataset is diagnosed with these 4 injuries (fractures, internal injuries, contusions abj, and lacertations), our focus will be on how age, race and location play a part with these injuries.

#cleanup
rm(vec, vecS, vecColor, df_diagnosisfreqSorted)

Fire Involvement and Alcohol

Related to the injuries are the cases of burns and alcohol having an effect on the falling injuries. Let’s take a look to see how the patient population in this dataset has been affected by them.We know from the diagnosis frequencies, that burn injuries

Fire Involvement

##frequency of the burn diagnosis
burncounts <- sqldf('SELECT diagnosis, count(diagnosis) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "47 - BURN, NOT SPEC." or diagnosis = "49 - BURN, CHEMICAL" or diagnosis = "48 - BURN, SCALD" or diagnosis = "51 - BURNS, THERMAL")
                        GROUP BY diagnosis
                        ORDER BY frequency DESC')

head(burncounts)

##frequency of fire_involvement
fcounts <-df_mapped[, c('fire_involvement')]

df_firefreq <- as.data.frame(table(fcounts))
colnames(df_firefreq)[1] <- "fire_involvement" 
colnames(df_firefreq)[2] <- "frequency" 

#sort the data in descending order
df_FireIsFreqSorted <- df_firefreq[order(-df_firefreq$frequency),]
head(df_FireIsFreqSorted)

#cleanup
rm(burncounts, fcounts, df_firefreq, df_FireIsFreqSorted)

From these numbers, we see there is very few burn injuries. Eightyone total, or 0.7% of the total dataset. For this project, we won’t explore burn injuries or fire involvement further because this is such a small part of the population.

Alcohol

Unlike the burns and fire involvement, there isn’t a set of injuries that specifically connects with alcohol usage. So, if we see a large number of patients that reported alcohol usage, it will be interesting to see which diagnosis were tied to it.

##frequency of the burn diagnosis
alcoholcounts <- sqldf('SELECT alcohol, count(alcohol) AS "frequency"
                        FROM dfmapping
                        GROUP BY alcohol
                        ORDER BY frequency DESC')

head(alcoholcounts)

yestotal <- alcoholcounts[2,2]

percent <- yestotal/rowcount*100
print(sprintf("Percentage of cases where alcohol was related to the falling injury: %.2f%%.", percent))
[1] "Percentage of cases where alcohol was related to the falling injury: 2.25%."
#cleanup
rm(alcoholcounts, yestotal, percent)

The number of alcohol related cases is 2588, or 2.25% of the patients in the dataset. For this project, we won’t explore alcohol further because this represents a small part of the population.

Locations Where Patients Reported Getting Hurt

I want to take a quick look at where people are getting hurt. This could impact how we look for correlations later on.


locationresults <- sqldf('SELECT location, count(location) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY")
                        GROUP BY location')

pie(locationresults$frequency, labels = locationresults$location, main="Location Breakdown")


#percentoffemales <- (gentable[2, 'Freq']/rowcount*100)
#ut <- sprintf("Percentage of females in this dataset: %f)", percentoffemales) #percentage of females in this dataset
#out

This pie chart isn’t the prettiest, but it does clearly show that Home is the primary location where injuries take place. This is followed up by Public and Unknown. Unknown is a frustrating result for this portion of the data, because it isn’t clear how to mitigate problems at this location.

Diagnosis, Race, and Gender

Let’s look to see if there is any correlation between the injuries, race, and gender, with the focus being on the top 4 injuries identified above.

From this plot, we can see that women are severely diagnosed with “57 - Fractures” and “63 - Internal Injury”; however, women suffer from all of these top diagnosis more than men. So special attention has to be paid for addressing the factors affecting women.

Diagnosis, Sex (Gender), and Location

So far, we’ve seen individual breakdowns of the data, but let’s start mixing up the columns in the hopes of exposing a culprete to the problem.

locationresults <- sqldf('SELECT location, diagnosis, sex, count(sex) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY")
                        GROUP BY location, diagnosis, sex')
sum(locationresults$frequency) #results = 99868
[1] 99868
    #<-    df_firefreq[order(-df_firefreq$frequency),]
sorted <- locationresults[order(-locationresults$frequency),]

head(sorted)

rm(locationresults, sorted)

This result is interesting because we see that fractures and internal injuries affect women by a significant amount at home.

Location, Diagnosis, Body Part

Now that we’ve identified females are being disproportional hurt at home, which body_part is affected the most?

I’m focused on the women, however, let’s continue to look at men in the query.

locationresults <- sqldf('SELECT location, diagnosis, body_part, sex, count(sex) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY")
                        GROUP BY location, diagnosis, body_part,  sex')
sum(locationresults$frequency) #results = 99868
[1] 99868
    #<-    df_firefreq[order(-df_firefreq$frequency),]
sorted <- locationresults[order(-locationresults$frequency),]

head(sorted)
NA

These results are helpful. Previously we saw fractures at the most serious problem. However, when we factor in the body_part we see the internal injuries to the head at home and in the public location is the most serious problem

rm(locationresults, sorted)

Let’s look at this same data with just the women.


locationresults <- sqldf('SELECT location, diagnosis, body_part, sex, count(sex) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY") AND
                                (sex = "FEMALE")
                        GROUP BY location, diagnosis, body_part,  sex')
sum(locationresults$frequency) #results = 99868
[1] 63248
    #<-    df_firefreq[order(-df_firefreq$frequency),]
sorted <- locationresults[order(-locationresults$frequency),]

head(sorted)
NA

From this table, we see that when the diagnosis is an internal injury, the head is the body part injuried the most. Home is the most likely place for the injuries, followed by the public or unknown locations. Next most frequent injury is the upper or lower trunk experiencing fractures.

Let do a similar query for the males to see how they are affected.


locationresults <- sqldf('SELECT location, diagnosis, body_part, sex, count(sex) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY") AND
                                (sex = "MALE")
                        GROUP BY location, diagnosis, body_part,  sex')
sum(locationresults$frequency) #results = 99868
[1] 36620
    #<-    df_firefreq[order(-df_firefreq$frequency),]
sorted <- locationresults[order(-locationresults$frequency),]

head(sorted)
NA

The results for men are very similar in that internal injuries to the head are hit the male population with the greatest frequency. Also, like the females, we see that males experience the fractures to the lower and upper trunk most frequently (after the head injuries), and the major of these injuries are at home.

Connections to Products?

Next, let’s see if there is a tie to the products.


locationresults <- sqldf('SELECT location, diagnosis, body_part, product_1,  sex, count(sex) AS "frequency"
                        FROM dfmapping
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY")
                        GROUP BY location, diagnosis, body_part, product_1,  sex')
#sum(locationresults$frequency) #results = 99868
    #<-    df_firefreq[order(-df_firefreq$frequency),]
sorted <- locationresults[order(-locationresults$frequency),]

head(sorted)
NA

This query shows the majority of products are tied to floors or flooring materials. Does this mean the patient just fell on the floor leading to the internal injuries and fractures? Also of note home, internal injury, and heads are the most likely to be the combination for our patients. Let’s look at the same data with just the floors.


locationresults <- sqldf('SELECT location, diagnosis, body_part, product_1,  count(product_1) AS "frequency"
                        FROM df_mapped
                        WHERE (diagnosis = "53 - CONTUSIONS, ABR." or diagnosis = "57 - FRACTURE" or diagnosis = "59 - LACERATION" or diagnosis = "62 - INTERNAL INJURY")
                                AND (product_1 = "1807 - FLOORS OR FLOORING MATERIALS")
                        GROUP BY location, diagnosis, body_part, product_1')
sorted <- locationresults[order(-locationresults$frequency),]
head(sorted)

sum <- sum(locationresults$frequency)
percent <- sum/rowcount * 100

print(   sprintf("The total frequency of the floor/ flooring materials injuries is %i, or %.2f%%.", sum, percent ))
[1] "The total frequency of the floor/ flooring materials injuries is 29348, or 25.49%."

As we combine the various data columns to our data exploration, we are seeing distinct groupings of the factors that make up this dataset.

Tieing Together the Data Exploration.

As previously observed, females at home, suffer internal injuries to their heads when landing on the floors. Let’s look at the revised dataset when we look at the most frequently sustain injuries.

sunburst_data <- data.frame(
  ids = c("Total", paste0(locationresults$sex, "-", locationresults$location), paste0(locationresults$sex, "-", locationresults$location, "-", locationresults$diagnosis), paste0(locationresults$sex, "-", locationresults$location, "-", locationresults$diagnosis, "-", locationresults$body_part)),
  labels = c("Total", paste0(locationresults$sex, "\n", locationresults$location), paste0(locationresults$diagnosis), paste0(locationresults$body_part)),
  parents = c("", rep("Total", 6), paste0(locationresults$sex, "-", locationresults$location), paste0(locationresults$sex, "-", locationresults$location, "-", locationresults$diagnosis)),
  values = c(sum(data$frequency), rep(NA, 6), locationresults$frequency),
  stringsAsFactors = FALSE
)
Error in data$frequency : object of type 'closure' is not subsettable

Body Part 2, Products 2, and Product 3

I’m reluctant to delve too deeply into these columns. First, these 3 columns are mostly blank, so where there is data in these columns, it seems to be a secondary result of the body_part and product_1. In other words, if we come up with a way to prevent injury to body_part with product_1, then we can prevent problems with body_part_2 and product_2 and product_3.

Addition Feature Engineering

library(waffle)

results <- sqldf('SELECT age, sex, count(sex) AS "frequency"
                        FROM df_mapped
                        GROUP BY age, sex')

ggplot(data5) + 
geom_point(mapping = aes(x=diagnosis, y=frequency, color=race, alpha=race, shape=sex)) + 
    labs(title = "Frequency of Injury By Diagnosis, Gender, & race <100", x="Diagnosis", y="Count")

Data Modeling with Hiearchical Model

Using ChatGPT for Analysis of the Narrative Data

LS0tDQp0aXRsZTogIkVtZXJnZW5jeSBSb29tIEZhbGxpbmcgSW5qdXJ5IEFuYWx5c2lzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KI0ludHJvZHVjdGlvbg0KVGhpcyBhbmFseXNpcyB3YXMgZG9uZSBieSBNaWtlIEdvbGR3ZWJlciwgU2VwdCAyMDIzLiBTdWJtaXR0ZWQgdG8gRHJpdmVuRGF0YS5vcmcgb24gT2N0IDYsIDIwMjMuIFRoaXMgZG9jdW1lbnQgc2hvd3M7DQp0aGUgc3RlcCBieSBzdGVwIHByb2Nlc3Mgb2YgYW5hbHl6aW5nIHRoZSBlbWVyZ2VuY3kgcm9vbSBkYXRhIHByb3ZpZGVkIGluIHRoZSBVbnN1cGVydmlzZWQgV2lzZG9tIGNvbnRlc3QuDQoNClRoZSBmdWxsIHNldCBvZiBwcm9qZWN0IGZpbGVzIGNhbiBiZSBmb3VuZCBhdDogaHR0cHM6Ly9naXRodWIuY29tL2hhbGNpYmVyL01hY2hpbmUtTGVhcm5pbmcvdHJlZS9tYXN0ZXIvVW5zdXBlcnZpc2VkV2lzZG9tLg0KDQojIERhdGEgSW5nZXN0aW9uDQpUaGUgZGF0YSB1c2VkIGluIHRoaXMgcHJvamVjdCBpcyB0aGUgUHJpbWFyeSBkYXRhIHNldC4gVGhpcyBjb250YWlucyAxMTUsMTI4IHJvd3Mgb2YgZGF0YS4gVGhlIGRhdGEgaXMgcHVsbGVkIGludG8gdHdvIGRhdGFmcmFtZXMuIE9uZSBjYWxsZWQgZGZfb3JpZ2luYWwsIGFuZCB0aGUgb3RoZXIgaXMgY2FsbGVkIGRmX21hcHBlZC4gVGhlIGRmX21hcHBlZCBzZXQgaXMgbW9kaWZpZWQgYnkgdGhlIGV4YW1wbGUgY29kZSBzbyB0aGF0IGl0IHByb2R1Y2VzIGh1bWFuIHJlYWRhYmxlIGRhdGEuIHRoZSBkZl9vcmlnaW5hbCBzZXQgd2lsbCBiZSBtb2RpZmllZCBieSB0aGUgZmVhdHVyZSBlbmdpbmVlciB0byBiZSB1c2FibGUgYnkgdGhlIG1vZGVsaW5nIGRvbmUgYmVsb3cuDQoNCg0KDQpgYGB7cn0NCiNZb3UnbGwgd2FudCB0byBhZGp1c3QgeW91ciBmaWxlIHBhdGggZm9yIHlvdXIgZW52aXJvbm1lbnQNCmZpbGVwYXRoIDwtICJjOlxcX3dvcmtpbmdcXE1hY2hpbmUtTGVhcm5pbmdcXFVuc3VwZXJ2aXNlZFdpc2RvbVxcRGF0YVxccHJpbWFyeV9kYXRhLmNzdiINCg0KZGZfb3JpZ2luYWwgPC0gcmVhZC5jc3YoZmlsZXBhdGgpDQoNCiN3ZSdsbCB1c2UgdGhpcyB2YXJpYWJsZSBmcmVxdWVudGx5DQpyb3djb3VudCA8LSBucm93KGRmX21hcHBlZCkNCmBgYA0KDQojIyBTZXR0aW5nIHVwIHRoZSBjYXRlZ29yaWNhbCBtYXBwaW5nIGRhdGFmcmFtZQ0KVGhlIGNvZGUgZm9yIHRoZSBuZXh0IHR3byBibG9ja3Mgd2FzIHRha2UgZGlyZWN0bHkgZnJvbSB0aGUgQ29tbXVuaXR5IENvZGUgc2VjdGlvbiBvZiB0aGUgY29udGVzdCB3ZWJzaXRlLCBmb3VuZCBhdDogaHR0cHM6Ly93d3cuZHJpdmVuZGF0YS5vcmcvY29tcGV0aXRpb25zLzIxNy9jZGMtZmFsbC1uYXJyYXRpdmVzL2NvbW11bml0eS1jb2RlLy5UaGUgcHVycG9zZSBvZiB0aGlzIGJsb2NrIGlzIHRvIHRha2UgdGhlIEpTT04gY29kaW5nIGluZm9ybWF0aW9uIGFuZCBtYXRjaCBpdCB0byB0aGUgbnVtZXJpYyB2YWx1ZXMgZm91bmQgaW4gcHJpbWFyeV9kYXRhLmNzdi4gSSd2ZSBmb3VuZCB0aGlzIHZlcnNpb24gb2YgdGhlIGRhdGFzZXQgdG8gYmUgaGVscGZ1bCBpbiB1bmRlcnN0YW5kaW5nIHRoZSBkYXRhLiBGb3IgZXhhbXBsZSwgIjU3IC0gRlJBQ1RVUkUiIGlzIG1vcmUgbWVhbmluZ2Z1bCB0aGFuIHRoZSBvcmlnaW5hbCAiNTciLiBNeSBpbnRlbnRpb24gaXMgdG8gdXNlIHRoZSBkZl9tYXBwZWQgZGF0YWZyYW1lIHByaW1hcmlseSB3aXRoIHRoZSBkYXRhIGV4cGxvcmF0aW9uIGFuZCB0aGUuIGRmX29yaWdpbmFsLCBjcmVhdGVkIGluIHRoZSBjb2RlIGJsb2NrIGFib3ZlIHdpbGwgYmUgdXNlZCBmb3IgdGhlIG1vZGVsaW5nIHdvcmsgYWZ0ZXIgbW9kaWZpY2F0aW9uIGluIHRoZSBGZWF0dXJlIEVuZ2luZWVyIHNlY3Rpb24gYmVsb3cuDQoNCmBgYHtyfQ0KbGlicmFyeShqc29ubGl0ZSkNCg0KbWFwcGluZ2ZpbGUgPC0gJ2M6XFxfd29ya2luZ1xcTWFjaGluZS1MZWFybmluZ1xcVW5zdXBlcnZpc2VkV2lzZG9tXFxEYXRhXFx2YXJpYWJsZV9tYXBwaW5nLmpzb24nDQptYXBwaW5nIDwtIGZyb21KU09OKG1hcHBpbmdmaWxlKQ0KbmFtZXMobWFwcGluZykNCg0KIyBDb252ZXJ0IHRvIGRhdGEgZnJhbWVzIHNvIHdlIGNhbiB1c2UgaW4gam9pbnMNCm1hcHBpbmdfdGFibGVzIDwtIGxpc3QoKQ0KZm9yIChjb2wgaW4gbmFtZXMobWFwcGluZykpIHsNCiAgICBtYXBwaW5nX3RhYmxlc1tbY29sXV0gPC0gZGF0YS5mcmFtZSgNCiAgICAgICAgaW5kPWFzLmludGVnZXIobmFtZXMobWFwcGluZ1tbY29sXV0pKSwgICMgY2hhbmdlIHRvIGludGVnZXIgdHlwZXMNCiAgICAgICAgdmFsdWVzPXVubGlzdChtYXBwaW5nW1tjb2xdXSkNCiAgICApDQp9DQpgYGANCg0KTm93IHRoYXQgdGhlIEpTT04gaW5mb3JtYXRpb24gaGFzIGJlZW4gaW5nZXN0ZWQsIHdlJ2xsIG1hcCB0aGUgY2F0ZWdvcmllcyBvbiB0byB0aGUgZGF0YWZyYW1lLCB3aGljaCB3ZSB3aWxsIGNhbGwgKipkZl9tYXBwZWQqKi4NCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCg0KIyBMb2FkIHByaW1hcnkgZGF0YQ0KZGZfbWFwcGVkIDwtIGRmX3ByaW1hcnkNCg0KIyBKb2luIGFuZCByZXBsYWNlIGVuY29kZWQgY29sdW1uDQpmb3IgKGNvbCBpbiBuYW1lcyhtYXBwaW5nKSkgew0KICAgIGRmX21hcHBlZCA8LSBkZl9tYXBwZWQgJT4lDQogICAgICAgIGxlZnRfam9pbihtYXBwaW5nX3RhYmxlc1tbY29sXV0sIGJ5PXNldE5hbWVzKCJpbmQiLCBjb2wpKSAlPiUNCiAgICAgICAgbXV0YXRlKCEhY29sIDo9IHZhbHVlcykgJT4lDQogICAgICAgIHNlbGVjdCgtdmFsdWVzKQ0KfQ0KDQoNCmBgYA0KDQoNCg0KDQoNCiMgRGF0YSBDbGVhbiBVcC8gUHJlbGltaW5hcnkgRmVhdHVyZSBFbmdpbmVlcg0KTGV0J3MgbG9vayBhdCB0aGUgZGF0YXNldCB0byBnZXQgYW4gaW5pdGlhbCBmZWVsIGZvciB0aGUgZGF0YSBxdWFsaXR5IGJ5IHJ1bm5pbmcgYSBzdW1tYXJ5DQoNCmBgYHtyfQ0Kc3VtbWFyeShkZl9vcmlnaW5hbCkNCmBgYA0KVXN1YWxseSBhIGNvbmNlcm4gaXMgbWlzc2luZyBkYXRhIHNjYXR0ZXJlZCByYW5kb21seSB0aG91Z2h0b3V0IHRoZSBzZXQuIEluIHRoaXMgY2FzZSwgdGhlIHN1bW1hcnkgc2hvd3MgdGhlIGRhdGEgaXMgaW4gZ29vZCBzaGFwZS4gVGhhdCBpcyB0aGVyZSBpc24ndCBtdWNoIGluIHRoZSB3YXkgb2YgbWlzc2luZyBkYXRhLiBUaGUgb25seSBOQSdzIHdlIHNlZSBhcmUgaW4gdGhlIGRpYWdub3Npc18yIGFuZCBib2R5X3BhcnRfMiBjb2x1bW5zLiBUaGlzIGlzbid0IGEgc3VwcmlzZSwgYmVjYXVzZSB0aGUgRVIgcGF0aWVudHMgZGlkbid0IG5lY2Vzc2FyaWx5IHN1ZmZlciBzZWNvbmRhcnkgaW5qdXJpZXMuIFRoZXJlIGlzIGEgY29ycmVsYXRpb24gYmV0d2VlbiB0aGVzZSB0d28gY29sdW1ucywgZ2l2ZW4gdGhlIGlkZW50aWNhbCBudW1iZXIgb2YgTkFzIGF0IDcxLDk4My4gVGhlc2UgY29sdW1ucyB3aWxsIGhhdmUgdG8gYmUgZXhwbG9yZWQgZnVydGhlciB0byBkZXRlcm1pbmUgaWYgaXQgc2hvdWxkIGJlIHVzZWQgaW4gb3VyIG1vZGVsaW5nLg0KDQpIb3dldmVyLCBnbGFuY2luZyBhdCB0aGUgZGF0YSBzZXRzIGRpcmVjdGx5IHNob3dzIGdhcHMgaW4gc29tZSBvZiB0aGUgb3RoZXIgY29sdW1ucy4gRm9yIGV4YW1wbGUgYm9keV9wYXJ0XzIsIG90aGVyX2RpYWdub3NpcyBhbmQgb3RoZXJfZGlhZ25vc2lzXzIgY29udGFpbiBtYW55IGdhcHMuDQoNCmBgYHtyfQ0KI3Njcm9sbCB0byB0aGUgcmlnaHQgdG8gc2VlIHRoZSBlbXB0eSBjb2x1bW5zIChleGFtcGxlIGJvZHlfcGFydF8yLCBvdGhlcl9kaWFnbm9zaXMgYW5kIG90aGVyX2Rpc2Fnbm9zaXNfMikuDQpoZWFkKGRmX21hcHBlZCkNCmBgYA0KDQoNCiMgRGF0YSBFeHBsb3JhdGlvbg0KVGhpcyBpcyBhY3R1YWxseSB0aGUgY3JpdGljYWwgcG9ydGlvbiBvZiB0aGUgcHJvamVjdCwgYW5kIG1vc3Qgb2YgdGhlIGVmZm9ydCB3YXMgc3BlbnQgb24gdGhpcyB3b3JrIGluIG9yZGVyIHRvIHVuZGVyc3RhbmQgdGhlIHNjb3BlIG9mIHRoZSBwcm9ibGVtLiBUaGUgcmVzdWx0cyBvZiB0aGlzIGV4cGxvcmF0aW9uIGFmZmVjdGVkIGxhdGVyIHBvcnRpb25zIG9mIHRoZSBleHBsb3JhdGlvbi4gUGxlYXNlIG5vdGUsIHRoZSBuYXJyYXRpdmUgY29sdW1uIHdvbid0IGJlIGxvb2tlZCBhdCBpbiB0aGUgZGF0YSBleHBsb3JhdGlvbi4gVGhlIGdvYWwgaXMgdG8gdXNlIENoYXRHUFQgdG8gYW5hbHl6ZSAgdGhpcyBkYXRhIGFuZCByZXR1cm4gaGVscGZ1bCByZXN1bHRzLiBTbywgdGhpcyBwYXJ0IG9mIHRoZSBkYXRhIGlzIGJlaW5nIGhhbmRsZWQgc2VwYXJhdGVseS4NCg0KTGV0J3MgYmVnaW4gYnkgbG9va2luZyBhdCBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgY29sdW1ucy4NCg0KIyMgQ29ycmVsYXRpb24gUGxvdA0KDQpgYGB7cn0NCmxpYnJhcnkoY29ycnBsb3QpDQoNCiN3ZSBuZWVkIHRvIHVzZSBudW1lcmljYWwgdmFsdWVzIGZvciB0aGUgY29ycmVsYXRpb24uIFNvLCB3ZSdsbCBtYWtlIGEgc3Vic2V0IG9mIHRoZSBkYXRhLg0KZGZfbnVtc3Vic2V0PC0gZGZfb3JpZ2luYWxbLCBjKDQ6NiwgODo5LCAxMywgMTU6MjIpXQ0KDQojdmlzdWFsaXphdGlvbiBtYXRyaXggb2YgdGhlIGRhdGEsIGxvb2tpbmcgZm9yIGNvcnJlbGF0aW9ucw0KY29ycnBsb3QoY29yKGRmX251bXN1YnNldCksIHR5cGU9ICJ1cHBlciIpDQpgYGANCldlIHNlZSBzdHJvbmcgY29ycmVsYXRpb25zIGJldHdlZW4gcmFjZSBhbmQgaGlzcGFuaWMsIHByb2R1Y3RfMSBhbmQgcHJvZHVjdF8yLCBhcyB3ZWxsIGFzIHByb2R1Y3RfMiBhbmQgcHJvZHVjdF8zLiBIb25lc3RseSwgdGhpcyBkb2Vzbid0IHNlZW0gdG8gYmUgdmVyeSBoZWxwZnVsLiBBdCBsZWFzdCBhcyB0aGlzIHN0YWdlLiBPdXIgZm9jdXMgaXMgb24gdGhlIGluanVyaWVzLiBUaGVyZSBhcmUgc29tZSBjb25uZWN0aW9ucyB0byBhZ2UgYW5kIHNvbWUgb2YgdGhlIG90aGVyIGZhY3RvcnMuIEluY2x1ZGluZyBhbGNvaG9sLiBTbywgd2UnbGwgaGF2ZSB0byBleHBsb3JlIHRoaXMuDQoNCiMjIEFnZQ0KTGV0J3MgbG9vayBhdCB0aGUgYWdlIGNhdGVnb3J5LiBJbiBwYXJ0aWN1bGFyLCBsZXQncyBzZWUgaWYgdGhlcmUgaXMgYSBwYXJ0aWN1bGFyIGFnZSB0aGF0IGlzIGhpdCBoYXJkZXIgdGhhbiBhbm90aGVyIGFnZS4gVGhlIGJsb2NrIGNvbnZlcnRzIGl0IGludG8gYSB0YWJsZSwgYW5kIHRoZSBwbG90IHNob3dzIHRoZSBmcmVxdWVuY3kgb2YgaW5qdXJpZXMgYnkgYWdlLg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCg0KI2ZyZXF1ZW5jeSBvZiBpbmp1cmllcyBieSBhZ2UNCmRhdGEgPC0gZGZfbnVtc3Vic2V0WywgYygnYWdlJywgJ3NleCcpXQ0KZGZfYWdlZnJlcSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRhdGEkYWdlKSkNCmNvbG5hbWVzKGRmX2FnZWZyZXEpWzFdIDwtICJhZ2UiIA0KY29sbmFtZXMoZGZfYWdlZnJlcSlbMl0gPC0gImZyZXF1ZW5jeSIgDQojaGVhZChkZl9hZ2VmcmVxKQ0KZ2dwbG90KGRmX2FnZWZyZXEpICsgDQogICAgZ2VvbV9jb3VudChtYXBwaW5nID0gYWVzKHg9YWdlLCB5PWZyZXF1ZW5jeSkpICsgDQogICAgbGFicyh0aXRsZSA9ICJGcmVxdWVuY3kgb2YgSW5qdXJ5IEJ5IEFnZSIsIHg9IkFnZSIsIHk9IkNvdW50IikNCmBgYA0KV2hhdCBpcyBpbnRlcmVzdGluZyBhYm91dCB0aGlzIHBsb3QsIGl0IHNob3dzIHRoYXQgdGhlIGluanVyeSBmcmVxdWVuY3kgaXMgcmVsYXRpdmVseSBoaWdoIChvdmVyIDM1MDApIHVudGlsIGFnZSA4OC4gSSBhbSB3b25kZXJpbmcgaWYgdGhlIGZhbGwgb2ZmIGlzIGR1ZSB0byBzb21lIGVudmlyb25tZW50YWwgbW92ZW1lbnQsIG9yIGlzIHRoZSBwb3B1bGF0aW9uIG92ZXIgODggc2hyaW5raW5nPyBUaGVyZSBpcyBhIGJpdCBvZiBhIHBsYXRlYXUgZm9yIGFnZXMgNzEtODAsIHdoZXJlIGVhY2ggZ3JvdXAgaGFzIG92ZXIgNDAwMCBpbmp1cmllcywgZXhjZXB0IGZvciBhZ2UgNzYgd2l0aCAzOTkzIGluanVyaWVzLiANCg0KIyMgU2V4IChHZW5kZXIpDQpOZXh0LCBsZXQncyBsb29rIGF0IHRoZSBicmVha2Rvd24gb2Ygc2V4IChnZW5kZXIpIGluIHRoaXMgZGF0YXNldC4NCg0KYGBge3J9DQojNjMlIG9mIHRoZSBpbmp1cmllcyBhcmUgdG8gd29tZW4NCg0KZ2VuZGVybGFiZWxzIDwtIGMoIk1hbGUiLCAiRmVtYWxlIikNCmdlbmRlciA8LSBhcy52ZWN0b3IoZGZfb3JpZ2luYWxbICwnc2V4J10pDQpnZW50YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGdlbmRlcikpDQoNCmdlbnRhYmxlJGdlbmRlciA8LSBtYXB2YWx1ZXMoZ2VudGFibGUkZ2VuZGVyLCBjKCAiMSIsICIyIiksIGdlbmRlcmxhYmVscykNCg0KcGllKGdlbnRhYmxlJEZyZXEsIGxhYmVscyA9IGdlbmRlcmxhYmVscywgbWFpbj0iU2V4IEJyZWFrZG93biIpDQoNCnBlcmNlbnRvZmZlbWFsZXMgPC0gKGdlbnRhYmxlWzIsICdGcmVxJ10vcm93Y291bnQqMTAwKQ0Kb3V0IDwtIHNwcmludGYoIlBlcmNlbnRhZ2Ugb2YgZmVtYWxlcyBpbiB0aGlzIGRhdGFzZXQ6ICVmKSIsIHBlcmNlbnRvZmZlbWFsZXMpICNwZXJjZW50YWdlIG9mIGZlbWFsZXMgaW4gdGhpcyBkYXRhc2V0DQpvdXQNCg0KYGBgDQpUaGlzIHBsb3QgdmlzdWFsbHkgc2hvd3MgdGhlIGJyZWFrZG93biBvZiBzZXggaW4gdGhpcyBkYXRhc2V0LiBXZSBzZWUgdGhhdCBhdCAqKjYzLjElKiosIGZlbWFsZXMgcmVwcmVzZW50cyB0aGUgbWFqb3JpdHkgb2YgY2FzZXMgaW4gdGhpcyBkYXRhc2V0LiBTbywgd2UnbGwgcGF5IHBhcnRpY3VsYXIgYXR0ZW50aW9uIHRvIHRoZSBmYWN0b3JzIGFmZmVjdGluZyB0aGlzIHBhcnQgb2YgdGhlIHBvcHVsYXRpb24uDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQojY2xlYW51cA0Kcm0oZ2VuZGVyLCBnZW50YWJsZSwgZ2VuZGVybGFibGVzLCBvdXQpDQpgYGANCg0KDQoNCg0KDQojIyMgQWdlIGFuZCBHZW5kZXINCk5leHQsIGxldCdzIGxvb2sgYXQgdGhlIHNwbGl0IGJ5IGFnZSBhbmQgZ2VuZGVyLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc3FsZGYpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KDQpyZXN1bHRzIDwtIHNxbGRmKCdTRUxFQ1QgYWdlLCBzZXgsIGNvdW50KHNleCkgQVMgImZyZXF1ZW5jeSINCiAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZGZfbWFwcGVkDQogICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSBhZ2UsIHNleCcpDQoNCiMgcmVzaGFwZSB0aGUgZGF0YWZyYW1lIGludG8gYSBsb25nIGZvcm1hdA0KcmVzdWx0cyA8LSBtZWx0IChyZXN1bHRzLCBpZC52YXJzID0gYyAoInNleCIsICJhZ2UiKSkNCg0KIyBwbG90IHR3byBsaW5lcyBmb3IgZGlmZmVyZW50IGdlbmRlcnMNCmdncGxvdCAocmVzdWx0cywgYWVzICh4ID0gYWdlLCB5ID0gdmFsdWUsIGdyb3VwID0gc2V4LCBjb2xvciA9IHNleCkpICsNCiAgZ2VvbV9saW5lICgpICsNCiAgbGFicyAoeCA9ICJBZ2UiLCB5ID0gIkluanVyeSBGcmVxdWVuY3kiLCBjb2xvciA9ICJTZXgiKQ0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfSAgDQpybShyZXN1bHRzKQ0KYGBgDQoNCg0KVGhpcyBjaGFydCBzaG93cyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYnkgZ2VuZGVyIGFjcm9zcyB0aGUgYWdlcyBvZiB0aGUgcGF0aWVudHMuIEZpcnN0LCB0aGlzIHBsb3QgY29uZmlybXMgdGhlIHByZXZpb3VzIHBpZSBjaGFydCBwbG90IGJ5IHNob3dpbmcgdGhlIGZlbWFsZSBwb3B1bGF0aW9uIHN1ZmZlcnMgZ3JlYXRlciBudW1iZXJzIG9mIGluanVyaWVzICphY3Jvc3MgdGhlIGFnZSBzcGVjdHJ1bSouIEFzIHdlIGV4cGxvcmUgZnVydGhlciwgd2UnbGwgaGF2ZSB0byBjb25zaWRlciB0aGUgcG9zc2liaWxpdHkgb2YgdXNpbmcgZGlmZmVyZW50IGFwcHJvYWNoZXMgZm9yIGhlbHBpbmcgZWFjaCBnZW5kZXIuIE9mIG5vdGUsIHRoaXMgc2hvd3MgdGhlIGRhdGEgc2V0IG9ubHkgaW5jbHVkZXMgbWFsZXMgYW5kIGZlbWFsZXMuIFRoZSBvdGhlciBjYXRlZ29yaWVzIGFyZSBub3QgcmVwcmVzZW50ZWQgaW4gdGhpcyBkYXRhc2V0Lg0KDQoNCiMjIERhdGEgRXhwbG9yYXRpb24gYnkgUmFjZSBhbmQgU2V4DQpOZXh0LCBsZXQncyBmYWN0b3IgaW4gdGhlIHJhY2Ugb2YgdGhlIHBhdGllbnRzIHRvIHNlZSBpZiBhbnkgcGFydGljdWxhciBncm91cCBpcyBhZmZlY3RlZCBtb3JlIHRoYW4gdGhlIG90aGVycy4gQWZ0ZXIgbG9va2luZyBhdCB0aGUgdXBwZXIgbGV2ZWwgZGF0YSBieSByYWNlLCB3ZSdsbCBsb29rIGF0IHRoZSBoaXNwYW5pYyBicmVha2Rvd24gYXQgdGhlIHBvcHVsYXRpb24uDQoNCg0KIyMjIEJyZWFrZG93biBCeSBSYWNlDQpXZSdsbCBzdGFydCBvZmYgd2l0aCBhIHNpbXBsZSBwaWUgY2hhcnQgdG8gc2VlLg0KDQoNCmBgYHtyfQ0KDQppZiAoc3lzdGVtLmZpbGUocGFja2FnZSA9ICJ3YWZmbGUiKSA9PSAiIikgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ3YWZmbGUiKQ0KfQ0KDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHdhZmZsZSkNCg0KcmNvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGRmX21hcHBlZCRyYWNlKSkNCmNvbG5hbWVzKHJjb3VudHMpWzFdPC0icmFjZSINCmNvbG5hbWVzKHJjb3VudHMpWzJdPC0iZnJlcXVlbmN5Ig0KDQp2ZWMgPC0gbnVtZXJpYygpDQp2ZWNTIDwtIGNoYXJhY3RlcigpDQoNCmZvcihpIGluIHJjb3VudHMkZnJlcXVlbmN5KQ0Kew0KICAgIHggPC0gaS9yb3djb3VudA0KICAgIHggPC0geCoxMDANCiAgICB2ZWMgPC0gYyh2ZWMsIHgpDQogICAgDQogICAgcyA8LSBpZmVsc2UoKHggPiA3LjApLCBzcHJpbnRmKCIlLjJmJSUiLCB4KSwgIiIpI2Rpc3BsYXkgb25seSBtZWFuaW5nIHZhbHVlcyBvbiB0aGUgcGllIGNoYXJ0DQogICAgdmVjUyA8LSBjKHZlY1MsIHMpDQp9DQoNCnJjb3VudHMgPC0gY2JpbmQocmNvdW50cywgbmV3X2NvbCA9IHZlYykNCmNvbG5hbWVzKHJjb3VudHMpWzNdPC0icGVyY2VudCINCg0KcmNvdW50cyA8LSBjYmluZChyY291bnRzLCBuZXdfY29sID0gdmVjUykNCmNvbG5hbWVzKHJjb3VudHMpWzRdPC0ibGFiZWxzIg0KDQpnZ3Bsb3QocmNvdW50cywgYWVzKHggPSAiIiwgeSA9IHBlcmNlbnQsIGZpbGwgPSByYWNlKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTEpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVscyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNFNTk4NjYiLCAiI0ZDRjNDRiIsICIjQUY2MDFBIiwgIiNBRUQ2RjEiLCAiI0JEMDAyNiIsICIjMjdBRTYwIiwgIiNGQUQ3QUQiKSkgKyAjI0U1OTg2Ng0KICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsgDQogIGxhYnModGl0bGUgPSAiQnJlYWtkb3duIG9mIHRoZSBQYXRpZW50IFBvcHVsYXRpb24gYnkgUmFjZSIsDQogICAgICAgICAgI3N1YnRpdGxlID0gInRlc3QiLA0KICAgICAgICAgIGNhcHRpb24gPSAiMzQuNCUgZGlkIG5vdCBzdGF0ZSB0aGVpciByYWNlLCBtYWtpbmcgYSByYWNpYWwgY29ycmVsYXRpb24gZGlmZmljdWx0IikNCmBgYCAgICANCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9ICANCnJtKHZlYywgdmVjUywgeCwgcywgcmNvdW50cykNCmBgYA0KVGhpcyBjaGFydCBpcyBwcm9ibGVtYXRpYywgYmVjYXVzZSBvZiB0aGUgaGlnaCBwZXJjZW50YWdlIG9mIE4uUy4gKG5vdCBzdGF0ZWQpLiBTbyBpdCBtYXliZSBkaWZmaWN1bHQgdG8gYWNjdXJhdGVseSBpZGVudGlmeSBhIHJhY2lhbCBjb21wb25lbnQgdG8gdGhlc2UgaW5qdXJpZXMuIA0KDQojIyMgSGlzcGFuaWMNCg0KDQpgYGB7cn0NCmlmIChzeXN0ZW0uZmlsZShwYWNrYWdlID0gIndhZmZsZSIpID09ICIiKSB7DQogIGluc3RhbGwucGFja2FnZXMoIndhZmZsZSIpDQp9DQoNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkod2FmZmxlKQ0KDQpoY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZGZfbWFwcGVkJGhpc3BhbmljKSkNCmNvbG5hbWVzKGhjb3VudHMpWzFdPC0iaGlzcGFuaWMiDQpjb2xuYW1lcyhoY291bnRzKVsyXTwtImZyZXF1ZW5jeSINCg0KdmVjIDwtIG51bWVyaWMoKQ0KdmVjUyA8LSBjaGFyYWN0ZXIoKQ0KDQpmb3IoaSBpbiBoY291bnRzJGZyZXF1ZW5jeSkNCnsNCiAgICB4IDwtIGkvcm93Y291bnQNCiAgICB4IDwtIHgqMTAwDQogICAgdmVjIDwtIGModmVjLCB4KQ0KICAgIA0KICAgIHMgPC0gc3ByaW50ZigiJS4yZiUlIiwgeCkNCiAgICB2ZWNTIDwtIGModmVjUywgcykNCn0NCg0KaGNvdW50cyA8LSBjYmluZChoY291bnRzLCBuZXdfY29sID0gdmVjKQ0KY29sbmFtZXMoaGNvdW50cylbM108LSJwZXJjZW50Ig0KDQpoY291bnRzIDwtIGNiaW5kKGhjb3VudHMsIG5ld19jb2wgPSB2ZWNTKQ0KY29sbmFtZXMoaGNvdW50cylbNF08LSJsYWJlbHMiDQoNCmdncGxvdChoY291bnRzLCBhZXMoeCA9ICIiLCB5ID0gcGVyY2VudCwgZmlsbCA9IGhpc3BhbmljKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTMpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVscyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNFNTk4NjYiLCIjMjdBRTYwIiwgIiNGQ0YzQ0YiKSkgKyAgIA0KICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApICsgDQogIGxhYnModGl0bGUgPSAiQnJlYWtkb3duIG9mIHRoZSBIaXNwYW5pYyBQb3B1bGF0aW9uIiwNCiAgICAgICAgICAjc3VidGl0bGUgPSAidGVzdCIsDQogICAgICAgICAgY2FwdGlvbiA9ICJPbmx5IDMlIG9mIHRoZSBwYXRpZW50cyBpZGVudGlmeSBhcyBIaXNwYW5pYyIpDQpgYGANClNvLCB0aGUgbWFqb3JpdHkgb2YgcGF0aWVudHMgYXJlIG5vdCBoaXNwYW5pYywgaG93ZXZlciwgd2UgY2FuJ3QgY29uY2x1ZGUgbm90IGJlaW5nIGhpc3BhbmljIG1ha2VzIGEgcGVyc29uIG1vcmUgbGlrZXkgdG8gZmFsbC4NCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KI2NsZWFudXANCnJtKHZlYywgdmVjUywgeCwgcywgaGNvdW50cykNCmBgYA0KDQoNCmBgYHtyfQ0KaWYgKHN5c3RlbS5maWxlKHBhY2thZ2UgPSAibW9vbkJvb2siKSA9PSAiIikgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJtb29uQm9vayIpDQp9DQoNCmlmIChzeXN0ZW0uZmlsZShwYWNrYWdlID0gIndlYnIiKSA9PSAiIikgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ3ZWJyIikNCn0NCg0KbGlicmFyeShtb29uQm9vaykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkod2VicikNCmxpYnJhcnkoZHBseXIpDQoNCnJoY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZGZfbWFwcGVkJGhpc3BhbmljLCBkZl9tYXBwZWQkcmFjZSkpDQpjb2xuYW1lcyhyaGNvdW50cylbMV08LSJoaXNwYW5pYyINCmNvbG5hbWVzKHJoY291bnRzKVsyXTwtInJhY2UiDQpjb2xuYW1lcyhyaGNvdW50cylbM108LSJmcmVxdWVuY3kiDQoNCndlYnI6OlBpZURvbnV0KHJoY291bnRzLCBhZXMoaGlzcGFuaWMsIHJhY2UsIGNvdW50PWZyZXF1ZW5jeSksIA0KICAgICAgICAgICAgICByMCA9IDAuMywgI2hpZGVzIHRoZSBjZW50ZXINCiAgICAgICAgICAgICAgIHIxID0gMC43LA0KICAgICAgICAgICAgICAgI2V4cGxvZGUgPSAzLA0KICAgICAgICAgICAgICAgI2V4cGxvZGVEb251dCA9IFRSVUUsDQogICAgICAgICAgICAgICBwaWVMYWJlbFNpemUgPSAzLA0KICAgICAgICAgICAgICAgZG9udXRMYWJlbFNpemUgPSA0LA0KICAgICAgICAgICAgICAgdGl0bGUgPSAiSGlzcGFuaWMgYW5kIFJhY2lhbCBCcmVha2Rvd24iKQ0KDQpgYGANCkFzIHdpdGggdGhlIEhpc3BhbmljIHBpZSBjaGFydCwgYW5kIHdpdGggb25seSAzJSBvZiB0aGUgaW5qdXJlZCBzZXQgYmVpbmcgaGlzcGFuaWMsIEknbSBub3QgY29udmluY2VkIHRoYXQgaGlzcGFuaWM9eWVzIGNvcnJlbGF0ZXMgdG8gYSBmYWxsLiBGdXJ0aGVyLCBubyBvbmUgcmFjaWFsIGdyb3VwIHN0YW5kcyBvdXQgQ2VydGFpbmx5LCBwYXJ0IG9mIHRoZSBVbmsvTm90IFN0YXRlZCBncm91cCBpcyBwcm9iYWJseSBoaXNwYW5pYywgYnV0IHdlIGRvbid0IGtub3cgZm9yIGNlcnRhaW4uIEZ1cnRoZXIgb2JmdXNjYXRpbmcgdGhpcyBwYXJ0IG9mIHRoZSBoaXNwYW5pYyBzZXQgaXMgdGhlIE4uUyBwYXJ0IG9mIHRoZSByYWNlIGRhdGEsIHdoaWNoIHJlcHJlc2VudHMgNzYlIG9mIHRoZSBVbmtub3duIGhpc3BhbmljIGdyb3VwLiANCg0KDQpXZSdsbCBjb250aW51ZSB0byBsb29rIGF0IHJhY2UsIGJ1dCBJIGRvbid0IGJlbGlldmUgdGhlcmUgaXMgYW55dGhpbmcgdG8gYmUgZ2FpbmVkIGZyb20gaW5jbHVkaW5nIHRoZSBoaXNwYW5pYyBjb2x1bW4gaW4gb3VyIG1vZGVsLg0KDQoNCg0KYGBge3IsIGlnbm9yZT1UUlVFfQ0Kcm0ocmhjb3VudHMpDQpgYGANCg0KDQoNCiMjIERpYWdub3Npcw0KTmV4dCBpcyBhIGxvb2sgYXQgdGhlIGRpYWdub3NpcyBjb2x1bW4uIFdlJ2xsIHN0YXJ0IG9mZiBieSBsb29raW5nIGZvciB0aGUgbW9zdCBoZWF2aWx5IGRpYWdub3NlZCBpbmp1cmllcy4NCg0KYGBge3J9DQojZ3JvdXBpbmcgdGhlIGRpYWdub3NpcyBjb2x1bW4sIGxvb2tpbmcgZm9yIHRoZSBtb3N0IGZyZXF1ZW50IGluanVyeQ0KDQojI2ZyZXF1ZW5jeSBvZiBlYWNoIGRpYWdub3Npcw0KZGNvdW50cyA8LWRmX21hcHBlZFssIGMoJ2RpYWdub3NpcycpXQ0KDQpkZl9kaWFnbm9zaXNmcmVxIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZGNvdW50cykpDQpjb2xuYW1lcyhkZl9kaWFnbm9zaXNmcmVxKVsxXSA8LSAiZGlhZ25vc2lzX2NvZGUiIA0KY29sbmFtZXMoZGZfZGlhZ25vc2lzZnJlcSlbMl0gPC0gImZyZXF1ZW5jeSIgDQoNCiNzb3J0IHRoZSBkYXRhIGluIGRlc2NlbmRpbmcgb3JkZXINCmRmX2RpYWdub3Npc2ZyZXFTb3J0ZWQgPC0gZGZfZGlhZ25vc2lzZnJlcVtvcmRlcigtZGZfZGlhZ25vc2lzZnJlcSRmcmVxdWVuY3kpLF0NCmhlYWQoZGZfZGlhZ25vc2lzZnJlcVNvcnRlZCkNCg0KI2NsZWFudXANCnJtKGRjb3VudHMsIGRmX2RpYWdub3Npc2ZyZXEpDQpgYGANCkZyb20gdGhpcyBzb3J0aW5nLCB3ZSBrbm93IHRoYXQgZnJhY3R1cmVzLCBpbnRlcm5hbCBpbmp1cmllcywgY29udHVzaW9ucyBhYmosIGFuZCBsYWNlcnRhdGlvbnMgbWFrZSB1cCB0aGUgbWFqb3JpdHkgb2YgdGhlIDI2IGxpc3RlZCBpbmp1cmllcyBieSBhIHNpZ25pZmljYW50IGFtb3VudC4NCg0KYGBge3J9DQojYmFzZWQgb24gdGhlIGZyZXF1ZW5jeSBvZiB0aGlzIHBsb3QsIHdlIGNhbiBzZWUgdGhhdCB0aGUgZmlyc3QgNCBpdGVtcyByZXByZXNlbnQgdGhlIG1ham9yaXR5IG9mIHRoZSBkaWFnbm9zaXMNCiNsZXRzIGxvb2sgYXQgdGhlIHBlcmNlbnRhZ2Ugb2YgdGhlIGNhc2VzDQoNCmhpZ2hlc3Q0ZGlhZ25vc2lzIDwtIHN1bShkZl9kaWFnbm9zaXNmcmVxU29ydGVkJGZyZXF1ZW5jeVsxOjRdKSAjIFRoaXMgZXF1YWxzIDk5LDg2OA0KcGVyY2VudG9mdG9wNGRpYWdub3NpcyA8LSBoaWdoZXN0NGRpYWdub3Npcy9yb3djb3VudCAgICAjMTE1MTI4PXRvdGFsIGluanVyaWVzIDg2LjclDQpwcmludChzcHJpbnRmKCJQZXJjZW50YWdlIG9mIHRoZSB0b3AgNCBpbmp1cmllcyBpczogJWYlJSwgb3IgJWkgdG90YWwgcmVwb3J0ZWQgaW5qdXJpZXMuIiwgcGVyY2VudG9mdG9wNGRpYWdub3NpcyoxMDAsIGhpZ2hlc3Q0ZGlhZ25vc2lzKSkNCg0KDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHdhZmZsZSkNCg0KDQpybSh2ZWMsIHZlY3MpDQoNCnZlYyA8LSBudW1lcmljKCkNCnZlY1MgPC0gY2hhcmFjdGVyKCkNCnZlY0NvbG9yIDwtIGNoYXJhY3RlcigpDQoNCm4gPC0gbnJvdyhkZl9kaWFnbm9zaXNmcmVxU29ydGVkKQ0KDQpmb3IoaSBpbiAxOm4pDQp7DQogICAgcm93IDwtIGRmX2RpYWdub3Npc2ZyZXFTb3J0ZWRbaSwgXQ0KICAgIA0KICAgIGNvZGUgPC0gYXMuY2hhcmFjdGVyKHJvdyRkaWFnbm9zaXNfY29kZSkNCiAgICBmcmVxIDwtIGFzLmludGVnZXIocm93JGZyZXF1ZW5jeSkNCiAgICANCiAgICBwcmludChjKGNvZGUsIGZyZXEpKQ0KICAgIA0KICAgIHBlcmMgPC0gKGZyZXEvcm93Y291bnQpKjEwMA0KICAgIA0KICAgIHZlYyA8LSBjKHZlYywgcGVyYykgI2FkZCB2YWx1ZSB0byBhIHZlY3Rvcg0KICAgIHNEaWFnIDwtIGlmZWxzZSgocGVyYyA+IDEwLjApLCBzcHJpbnRmKCIlcyIsIGNvZGUpLCAiIikjZGlzcGxheSBvbmx5IG1lYW5pbmcgdmFsdWVzIG9uIHRoZSBwaWUgY2hhcnQNCiAgICB2ZWNTIDwtIGModmVjUywgc0RpYWcpDQogICAgDQogICAgc0NvbG9yIDwtIGlmZWxzZSgoaSA8IDUpLCAiI0FBMDAwMCIsICIjMDAwMEFBIikNCiAgICB2ZWNDb2xvciA8LSBjKHZlY0NvbG9yLCBzQ29sb3IpICAgICAgICAgICAgICAgICAgICAgDQp9I2VuZCBvZiBhcHBseQ0KDQoNCmRmX2RpYWdub3Npc2ZyZXFTb3J0ZWQgPC0gY2JpbmQoZGZfZGlhZ25vc2lzZnJlcVNvcnRlZCwgbmV3X2NvbCA9IHZlYykNCmNvbG5hbWVzKGRmX2RpYWdub3Npc2ZyZXFTb3J0ZWQpWzNdPC0icGVyY2VudCINCg0KZGZfZGlhZ25vc2lzZnJlcVNvcnRlZCA8LSBjYmluZChkZl9kaWFnbm9zaXNmcmVxU29ydGVkLCBuZXdfY29sID0gdmVjUykNCmNvbG5hbWVzKGRmX2RpYWdub3Npc2ZyZXFTb3J0ZWQpWzRdPC0ibGFiZWxzIg0KDQpkZl9kaWFnbm9zaXNmcmVxU29ydGVkIDwtIGNiaW5kKGRmX2RpYWdub3Npc2ZyZXFTb3J0ZWQsIG5ld19jb2wgPSB2ZWNDb2xvcikNCmNvbG5hbWVzKGRmX2RpYWdub3Npc2ZyZXFTb3J0ZWQpWzVdPC0iY29sb3JzIg0KDQpnZ3Bsb3QoZGZfZGlhZ25vc2lzZnJlcVNvcnRlZCwgYWVzKHggPSAiIiwgeSA9IGZyZXF1ZW5jeSwgZmlsbCA9IGRpYWdub3Npc19jb2RlKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTEpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVscyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogICNzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBkZl9kaWFnbm9zaXNmcmVxU29ydGVkJGNvbG9ycykgKw0KICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApDQoNCmBgYA0KU2VlaW5nIHRoZSBtYWpvcml0eSAoODYlKSBvZiB0aGUgZGF0YXNldCBpcyBkaWFnbm9zZWQgd2l0aCB0aGVzZSA0IGluanVyaWVzIChmcmFjdHVyZXMsIGludGVybmFsIGluanVyaWVzLCBjb250dXNpb25zIGFiaiwgYW5kIGxhY2VydGF0aW9ucyksIG91ciBmb2N1cyB3aWxsIGJlIG9uIGhvdyBhZ2UsIHJhY2UgYW5kIGxvY2F0aW9uIHBsYXkgYSBwYXJ0IHdpdGggdGhlc2UgaW5qdXJpZXMuDQoNCmBgYHtyLCBpZ25vcmU9VFJVRX0NCiNjbGVhbnVwDQpybSh2ZWMsIHZlY1MsIHZlY0NvbG9yLCBkZl9kaWFnbm9zaXNmcmVxU29ydGVkKQ0KYGBgDQojIyMgRmlyZSBJbnZvbHZlbWVudCBhbmQgQWxjb2hvbA0KUmVsYXRlZCB0byB0aGUgaW5qdXJpZXMgYXJlIHRoZSBjYXNlcyBvZiBidXJucyBhbmQgYWxjb2hvbCBoYXZpbmcgYW4gZWZmZWN0IG9uIHRoZSBmYWxsaW5nIGluanVyaWVzLiBMZXQncyB0YWtlIGEgbG9vayB0byBzZWUgaG93IHRoZSBwYXRpZW50IHBvcHVsYXRpb24gaW4gdGhpcyBkYXRhc2V0IGhhcyBiZWVuIGFmZmVjdGVkIGJ5IHRoZW0uV2Uga25vdyBmcm9tIHRoZSBkaWFnbm9zaXMgZnJlcXVlbmNpZXMsIHRoYXQgYnVybiBpbmp1cmllcyANCg0KIyMjIyBGaXJlIEludm9sdmVtZW50DQpgYGB7cn0NCiMjZnJlcXVlbmN5IG9mIHRoZSBidXJuIGRpYWdub3Npcw0KYnVybmNvdW50cyA8LSBzcWxkZignU0VMRUNUIGRpYWdub3NpcywgY291bnQoZGlhZ25vc2lzKSBBUyAiZnJlcXVlbmN5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBkZl9tYXBwZWQNCiAgICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIChkaWFnbm9zaXMgPSAiNDcgLSBCVVJOLCBOT1QgU1BFQy4iIG9yIGRpYWdub3NpcyA9ICI0OSAtIEJVUk4sIENIRU1JQ0FMIiBvciBkaWFnbm9zaXMgPSAiNDggLSBCVVJOLCBTQ0FMRCIgb3IgZGlhZ25vc2lzID0gIjUxIC0gQlVSTlMsIFRIRVJNQUwiKQ0KICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgZGlhZ25vc2lzDQogICAgICAgICAgICAgICAgICAgICAgICBPUkRFUiBCWSBmcmVxdWVuY3kgREVTQycpDQoNCmhlYWQoYnVybmNvdW50cykNCg0KDQojI2ZyZXF1ZW5jeSBvZiBmaXJlX2ludm9sdmVtZW50DQpmY291bnRzIDwtZGZfbWFwcGVkWywgYygnZmlyZV9pbnZvbHZlbWVudCcpXQ0KDQpkZl9maXJlZnJlcSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGZjb3VudHMpKQ0KY29sbmFtZXMoZGZfZmlyZWZyZXEpWzFdIDwtICJmaXJlX2ludm9sdmVtZW50IiANCmNvbG5hbWVzKGRmX2ZpcmVmcmVxKVsyXSA8LSAiZnJlcXVlbmN5IiANCg0KI3NvcnQgdGhlIGRhdGEgaW4gZGVzY2VuZGluZyBvcmRlcg0KZGZfRmlyZUlzRnJlcVNvcnRlZCA8LSBkZl9maXJlZnJlcVtvcmRlcigtZGZfZmlyZWZyZXEkZnJlcXVlbmN5KSxdDQpoZWFkKGRmX0ZpcmVJc0ZyZXFTb3J0ZWQpDQoNCiNjbGVhbnVwDQpybShidXJuY291bnRzLCBmY291bnRzLCBkZl9maXJlZnJlcSwgZGZfRmlyZUlzRnJlcVNvcnRlZCkNCmBgYA0KRnJvbSB0aGVzZSBudW1iZXJzLCB3ZSBzZWUgdGhlcmUgaXMgdmVyeSBmZXcgYnVybiBpbmp1cmllcy4gRWlnaHR5b25lIHRvdGFsLCBvciAwLjclIG9mIHRoZSB0b3RhbCBkYXRhc2V0LiBGb3IgdGhpcyBwcm9qZWN0LCB3ZSB3b24ndCBleHBsb3JlIGJ1cm4gaW5qdXJpZXMgb3IgZmlyZSBpbnZvbHZlbWVudCBmdXJ0aGVyIGJlY2F1c2UgdGhpcyBpcyBzdWNoIGEgc21hbGwgcGFydCBvZiB0aGUgcG9wdWxhdGlvbi4NCg0KIyMjIyBBbGNvaG9sDQpVbmxpa2UgdGhlIGJ1cm5zIGFuZCBmaXJlIGludm9sdmVtZW50LCB0aGVyZSBpc24ndCBhIHNldCBvZiBpbmp1cmllcyB0aGF0IHNwZWNpZmljYWxseSBjb25uZWN0cyB3aXRoIGFsY29ob2wgdXNhZ2UuIFNvLCBpZiB3ZSBzZWUgYSBsYXJnZSBudW1iZXIgb2YgcGF0aWVudHMgdGhhdCByZXBvcnRlZCBhbGNvaG9sIHVzYWdlLCBpdCB3aWxsIGJlIGludGVyZXN0aW5nIHRvIHNlZSB3aGljaCBkaWFnbm9zaXMgd2VyZSB0aWVkIHRvIGl0Lg0KDQpgYGB7cn0NCiMjZnJlcXVlbmN5IG9mIHRoZSBidXJuIGRpYWdub3Npcw0KYWxjb2hvbGNvdW50cyA8LSBzcWxkZignU0VMRUNUIGFsY29ob2wsIGNvdW50KGFsY29ob2wpIEFTICJmcmVxdWVuY3kiDQogICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGRmX21hcHBlZA0KICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgYWxjb2hvbA0KICAgICAgICAgICAgICAgICAgICAgICAgT1JERVIgQlkgZnJlcXVlbmN5IERFU0MnKQ0KDQpoZWFkKGFsY29ob2xjb3VudHMpDQoNCnllc3RvdGFsIDwtIGFsY29ob2xjb3VudHNbMiwyXQ0KDQpwZXJjZW50IDwtIHllc3RvdGFsL3Jvd2NvdW50KjEwMA0KcHJpbnQoc3ByaW50ZigiUGVyY2VudGFnZSBvZiBjYXNlcyB3aGVyZSBhbGNvaG9sIHdhcyByZWxhdGVkIHRvIHRoZSBmYWxsaW5nIGluanVyeTogJS4yZiUlLiIsIHBlcmNlbnQpKQ0KI2NsZWFudXANCnJtKGFsY29ob2xjb3VudHMsIHllc3RvdGFsLCBwZXJjZW50KQ0KYGBgDQpUaGUgbnVtYmVyIG9mIGFsY29ob2wgcmVsYXRlZCBjYXNlcyBpcyAyNTg4LCBvciAyLjI1JSBvZiB0aGUgcGF0aWVudHMgaW4gdGhlIGRhdGFzZXQuIEZvciB0aGlzIHByb2plY3QsIHdlIHdvbid0IGV4cGxvcmUgYWxjb2hvbCBmdXJ0aGVyIGJlY2F1c2UgdGhpcyByZXByZXNlbnRzIGEgc21hbGwgcGFydCBvZiB0aGUgcG9wdWxhdGlvbi4NCg0KIyMgTG9jYXRpb25zIFdoZXJlIFBhdGllbnRzIFJlcG9ydGVkIEdldHRpbmcgSHVydA0KSSB3YW50IHRvIHRha2UgYSBxdWljayBsb29rIGF0IHdoZXJlIHBlb3BsZSBhcmUgZ2V0dGluZyBodXJ0LiBUaGlzIGNvdWxkIGltcGFjdCBob3cgd2UgbG9vayBmb3IgY29ycmVsYXRpb25zIGxhdGVyIG9uLg0KDQpgYGB7cn0NCg0KbG9jYXRpb25yZXN1bHRzIDwtIHNxbGRmKCdTRUxFQ1QgbG9jYXRpb24sIGNvdW50KGxvY2F0aW9uKSBBUyAiZnJlcXVlbmN5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBkZl9tYXBwZWQNCiAgICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIChkaWFnbm9zaXMgPSAiNTMgLSBDT05UVVNJT05TLCBBQlIuIiBvciBkaWFnbm9zaXMgPSAiNTcgLSBGUkFDVFVSRSIgb3IgZGlhZ25vc2lzID0gIjU5IC0gTEFDRVJBVElPTiIgb3IgZGlhZ25vc2lzID0gIjYyIC0gSU5URVJOQUwgSU5KVVJZIikNCiAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIGxvY2F0aW9uJykNCg0KcGllKGxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3ksIGxhYmVscyA9IGxvY2F0aW9ucmVzdWx0cyRsb2NhdGlvbiwgbWFpbj0iTG9jYXRpb24gQnJlYWtkb3duIikNCg0KI3BlcmNlbnRvZmZlbWFsZXMgPC0gKGdlbnRhYmxlWzIsICdGcmVxJ10vcm93Y291bnQqMTAwKQ0KI3V0IDwtIHNwcmludGYoIlBlcmNlbnRhZ2Ugb2YgZmVtYWxlcyBpbiB0aGlzIGRhdGFzZXQ6ICVmKSIsIHBlcmNlbnRvZmZlbWFsZXMpICNwZXJjZW50YWdlIG9mIGZlbWFsZXMgaW4gdGhpcyBkYXRhc2V0DQojb3V0DQpgYGANClRoaXMgcGllIGNoYXJ0IGlzbid0IHRoZSBwcmV0dGllc3QsIGJ1dCBpdCBkb2VzIGNsZWFybHkgc2hvdyB0aGF0IEhvbWUgaXMgdGhlIHByaW1hcnkgbG9jYXRpb24gd2hlcmUgaW5qdXJpZXMgdGFrZSBwbGFjZS4gVGhpcyBpcyBmb2xsb3dlZCB1cCBieSBQdWJsaWMgYW5kIFVua25vd24uIFVua25vd24gaXMgYSBmcnVzdHJhdGluZyByZXN1bHQgZm9yIHRoaXMgcG9ydGlvbiBvZiB0aGUgZGF0YSwgYmVjYXVzZSBpdCBpc24ndCBjbGVhciBob3cgdG8gbWl0aWdhdGUgcHJvYmxlbXMgYXQgdGhpcyBsb2NhdGlvbi4NCg0KDQojIyBEaWFnbm9zaXMsIFJhY2UsIGFuZCBHZW5kZXINCkxldCdzIGxvb2sgdG8gc2VlIGlmIHRoZXJlIGlzIGFueSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBpbmp1cmllcywgcmFjZSwgYW5kIGdlbmRlciwgd2l0aCB0aGUgZm9jdXMgYmVpbmcgb24gdGhlIHRvcCA0IGluanVyaWVzIGlkZW50aWZpZWQgYWJvdmUuDQoNCmBgYHtyfQ0KaWYgKHN5c3RlbS5maWxlKHBhY2thZ2UgPSAic3FsZGYiKSA9PSAiIikgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJzcWxkZiIpDQp9DQoNCmxpYnJhcnkoc3FsZGYpDQpsaWJyYXJ5KHBseXIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojZnJlcXVlbmN5IG9mIGVhY2ggZGlhZ25vc2lzIGJ5IGdlbmRlcg0KcmVzdWx0cyA8LSBzcWxkZignU0VMRUNUIGRpYWdub3Npcywgc2V4LCBjb3VudCgqKSBBUyAiZnJlcXVlbmN5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBkZl9tYXBwZWQNCiAgICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIChkaWFnbm9zaXMgPSAiNTMgLSBDT05UVVNJT05TLCBBQlIuIiBvciBkaWFnbm9zaXMgPSAiNTcgLSBGUkFDVFVSRSIgb3IgZGlhZ25vc2lzID0gIjU5IC0gTEFDRVJBVElPTiIgb3IgZGlhZ25vc2lzID0gIjYyIC0gSU5URVJOQUwgSU5KVVJZIikNCiAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIGRpYWdub3Npcywgc2V4DQogICAgICAgICAgICAgICAgICAgICAgICBPUkRFUiBCWSBkaWFnbm9zaXMsIHNleCwgZnJlcXVlbmN5IERFU0MnKQ0KDQoNCiNwbG90IHRoZW0NCmdncGxvdChyZXN1bHRzKSArIA0KICAgIGdlb21fY291bnQobWFwcGluZyA9IGFlcyh4PWRpYWdub3NpcywgeT1mcmVxdWVuY3ksIGNvbG9yPXNleCkpICsgDQogICAgbGFicyh0aXRsZSA9ICJGcmVxdWVuY3kgb2YgSW5qdXJ5IEJ5IERpYWdub3NpcyAmIEdlbmRlciIsIHg9IkRpYWdub3NpcyIsIHk9IkNvdW50IikNCmBgYA0KIEZyb20gdGhpcyBwbG90LCB3ZSBjYW4gc2VlIHRoYXQgd29tZW4gYXJlIHNldmVyZWx5IGRpYWdub3NlZCB3aXRoICI1NyAtIEZyYWN0dXJlcyIgYW5kICI2MyAtIEludGVybmFsIEluanVyeSI7IGhvd2V2ZXIsIHdvbWVuIHN1ZmZlciBmcm9tIGFsbCBvZiB0aGVzZSB0b3AgZGlhZ25vc2lzIG1vcmUgdGhhbiBtZW4uICoqU28gc3BlY2lhbCBhdHRlbnRpb24qKiBoYXMgdG8gYmUgcGFpZCBmb3IgYWRkcmVzc2luZyB0aGUgZmFjdG9ycyBhZmZlY3Rpbmcgd29tZW4uDQogDQojIyBEaWFnbm9zaXMsIFNleCAoR2VuZGVyKSwgYW5kIExvY2F0aW9uIA0KU28gZmFyLCB3ZSd2ZSBzZWVuIGluZGl2aWR1YWwgYnJlYWtkb3ducyBvZiB0aGUgZGF0YSwgYnV0IGxldCdzIHN0YXJ0IG1peGluZyB1cCB0aGUgY29sdW1ucyBpbiB0aGUgaG9wZXMgb2YgZXhwb3NpbmcgYSBjdWxwcmV0ZSB0byB0aGUgcHJvYmxlbS4NCg0KYGBge3J9DQpsb2NhdGlvbnJlc3VsdHMgPC0gc3FsZGYoJ1NFTEVDVCBsb2NhdGlvbiwgZGlhZ25vc2lzLCBzZXgsIGNvdW50KHNleCkgQVMgImZyZXF1ZW5jeSINCiAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZGZfbWFwcGVkDQogICAgICAgICAgICAgICAgICAgICAgICBXSEVSRSAoZGlhZ25vc2lzID0gIjUzIC0gQ09OVFVTSU9OUywgQUJSLiIgb3IgZGlhZ25vc2lzID0gIjU3IC0gRlJBQ1RVUkUiIG9yIGRpYWdub3NpcyA9ICI1OSAtIExBQ0VSQVRJT04iIG9yIGRpYWdub3NpcyA9ICI2MiAtIElOVEVSTkFMIElOSlVSWSIpDQogICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSBsb2NhdGlvbiwgZGlhZ25vc2lzLCBzZXgnKQ0Kc3VtKGxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpICNyZXN1bHRzID0gOTk4NjgNCiAgICAjPC0gICAgZGZfZmlyZWZyZXFbb3JkZXIoLWRmX2ZpcmVmcmVxJGZyZXF1ZW5jeSksXQ0Kc29ydGVkIDwtIGxvY2F0aW9ucmVzdWx0c1tvcmRlcigtbG9jYXRpb25yZXN1bHRzJGZyZXF1ZW5jeSksXQ0KDQpoZWFkKHNvcnRlZCkNCg0Kcm0obG9jYXRpb25yZXN1bHRzLCBzb3J0ZWQpDQoNCmBgYA0KVGhpcyByZXN1bHQgaXMgaW50ZXJlc3RpbmcgYmVjYXVzZSB3ZSBzZWUgdGhhdCBmcmFjdHVyZXMgYW5kIGludGVybmFsIGluanVyaWVzIGFmZmVjdCB3b21lbiBieSBhIHNpZ25pZmljYW50IGFtb3VudCAqKmF0IGhvbWUqKi4NCg0KDQojIExvY2F0aW9uLCBEaWFnbm9zaXMsIEJvZHkgUGFydA0KTm93IHRoYXQgd2UndmUgaWRlbnRpZmllZCBmZW1hbGVzIGFyZSBiZWluZyBkaXNwcm9wb3J0aW9uYWwgaHVydCBhdCBob21lLCB3aGljaCBib2R5X3BhcnQgaXMgYWZmZWN0ZWQgdGhlIG1vc3Q/IA0KDQpJJ20gZm9jdXNlZCBvbiB0aGUgd29tZW4sIGhvd2V2ZXIsIGxldCdzIGNvbnRpbnVlIHRvIGxvb2sgYXQgbWVuIGluIHRoZSBxdWVyeS4NCmBgYHtyfQ0KbG9jYXRpb25yZXN1bHRzIDwtIHNxbGRmKCdTRUxFQ1QgbG9jYXRpb24sIGRpYWdub3NpcywgYm9keV9wYXJ0LCBzZXgsIGNvdW50KHNleCkgQVMgImZyZXF1ZW5jeSINCiAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZGZfbWFwcGVkDQogICAgICAgICAgICAgICAgICAgICAgICBXSEVSRSAoZGlhZ25vc2lzID0gIjUzIC0gQ09OVFVTSU9OUywgQUJSLiIgb3IgZGlhZ25vc2lzID0gIjU3IC0gRlJBQ1RVUkUiIG9yIGRpYWdub3NpcyA9ICI1OSAtIExBQ0VSQVRJT04iIG9yIGRpYWdub3NpcyA9ICI2MiAtIElOVEVSTkFMIElOSlVSWSIpDQogICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSBsb2NhdGlvbiwgZGlhZ25vc2lzLCBib2R5X3BhcnQsICBzZXgnKQ0Kc3VtKGxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpICNyZXN1bHRzID0gOTk4NjgNCiAgICAjPC0gICAgZGZfZmlyZWZyZXFbb3JkZXIoLWRmX2ZpcmVmcmVxJGZyZXF1ZW5jeSksXQ0Kc29ydGVkIDwtIGxvY2F0aW9ucmVzdWx0c1tvcmRlcigtbG9jYXRpb25yZXN1bHRzJGZyZXF1ZW5jeSksXQ0KDQpoZWFkKHNvcnRlZCkNCg0KYGBgDQpUaGVzZSByZXN1bHRzIGFyZSBoZWxwZnVsLiBQcmV2aW91c2x5IHdlIHNhdyBmcmFjdHVyZXMgYXQgdGhlIG1vc3Qgc2VyaW91cyBwcm9ibGVtLiBIb3dldmVyLCB3aGVuIHdlIGZhY3RvciBpbiB0aGUgYm9keV9wYXJ0IHdlIHNlZSB0aGUgaW50ZXJuYWwgaW5qdXJpZXMgdG8gdGhlIGhlYWQgYXQgaG9tZSBhbmQgaW4gdGhlIHB1YmxpYyBsb2NhdGlvbiBpcyB0aGUgbW9zdCBzZXJpb3VzIHByb2JsZW0NCg0KYGBge3IsIGlnbm9yZT1UUlVFfQ0Kcm0obG9jYXRpb25yZXN1bHRzLCBzb3J0ZWQpDQpgYGANCg0KTGV0J3MgbG9vayBhdCB0aGlzIHNhbWUgZGF0YSB3aXRoIGp1c3QgdGhlIHdvbWVuLg0KYGBge3J9DQoNCmxvY2F0aW9ucmVzdWx0cyA8LSBzcWxkZignU0VMRUNUIGxvY2F0aW9uLCBkaWFnbm9zaXMsIGJvZHlfcGFydCwgc2V4LCBjb3VudChzZXgpIEFTICJmcmVxdWVuY3kiDQogICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGRmX21hcHBlZA0KICAgICAgICAgICAgICAgICAgICAgICAgV0hFUkUgKGRpYWdub3NpcyA9ICI1MyAtIENPTlRVU0lPTlMsIEFCUi4iIG9yIGRpYWdub3NpcyA9ICI1NyAtIEZSQUNUVVJFIiBvciBkaWFnbm9zaXMgPSAiNTkgLSBMQUNFUkFUSU9OIiBvciBkaWFnbm9zaXMgPSAiNjIgLSBJTlRFUk5BTCBJTkpVUlkiKSBBTkQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHNleCA9ICJGRU1BTEUiKQ0KICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgbG9jYXRpb24sIGRpYWdub3NpcywgYm9keV9wYXJ0LCAgc2V4JykNCnN1bShsb2NhdGlvbnJlc3VsdHMkZnJlcXVlbmN5KSAjcmVzdWx0cyA9IDk5ODY4DQogICAgIzwtICAgIGRmX2ZpcmVmcmVxW29yZGVyKC1kZl9maXJlZnJlcSRmcmVxdWVuY3kpLF0NCnNvcnRlZCA8LSBsb2NhdGlvbnJlc3VsdHNbb3JkZXIoLWxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpLF0NCg0KaGVhZChzb3J0ZWQpDQoNCmBgYA0KRnJvbSB0aGlzIHRhYmxlLCB3ZSBzZWUgdGhhdCB3aGVuIHRoZSBkaWFnbm9zaXMgaXMgYW4gaW50ZXJuYWwgaW5qdXJ5LCB0aGUgaGVhZCBpcyB0aGUgYm9keSBwYXJ0IGluanVyaWVkIHRoZSBtb3N0LiBIb21lIGlzIHRoZSBtb3N0IGxpa2VseSBwbGFjZSBmb3IgdGhlIGluanVyaWVzLCBmb2xsb3dlZCBieSB0aGUgcHVibGljIG9yIHVua25vd24gbG9jYXRpb25zLiBOZXh0IG1vc3QgZnJlcXVlbnQgaW5qdXJ5IGlzIHRoZSB1cHBlciBvciBsb3dlciB0cnVuayBleHBlcmllbmNpbmcgZnJhY3R1cmVzLg0KDQpMZXQgZG8gYSBzaW1pbGFyIHF1ZXJ5IGZvciB0aGUgbWFsZXMgdG8gc2VlIGhvdyB0aGV5IGFyZSBhZmZlY3RlZC4NCmBgYHtyfQ0KDQpsb2NhdGlvbnJlc3VsdHMgPC0gc3FsZGYoJ1NFTEVDVCBsb2NhdGlvbiwgZGlhZ25vc2lzLCBib2R5X3BhcnQsIHNleCwgY291bnQoc2V4KSBBUyAiZnJlcXVlbmN5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBkZl9tYXBwZWQNCiAgICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIChkaWFnbm9zaXMgPSAiNTMgLSBDT05UVVNJT05TLCBBQlIuIiBvciBkaWFnbm9zaXMgPSAiNTcgLSBGUkFDVFVSRSIgb3IgZGlhZ25vc2lzID0gIjU5IC0gTEFDRVJBVElPTiIgb3IgZGlhZ25vc2lzID0gIjYyIC0gSU5URVJOQUwgSU5KVVJZIikgQU5EDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChzZXggPSAiTUFMRSIpDQogICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSBsb2NhdGlvbiwgZGlhZ25vc2lzLCBib2R5X3BhcnQsICBzZXgnKQ0Kc3VtKGxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpICNyZXN1bHRzID0gOTk4NjgNCiAgICAjPC0gICAgZGZfZmlyZWZyZXFbb3JkZXIoLWRmX2ZpcmVmcmVxJGZyZXF1ZW5jeSksXQ0Kc29ydGVkIDwtIGxvY2F0aW9ucmVzdWx0c1tvcmRlcigtbG9jYXRpb25yZXN1bHRzJGZyZXF1ZW5jeSksXQ0KDQpoZWFkKHNvcnRlZCkNCg0KYGBgDQpUaGUgcmVzdWx0cyBmb3IgbWVuIGFyZSB2ZXJ5IHNpbWlsYXIgaW4gdGhhdCBpbnRlcm5hbCBpbmp1cmllcyB0byB0aGUgaGVhZCBhcmUgaGl0IHRoZSBtYWxlIHBvcHVsYXRpb24gd2l0aCB0aGUgZ3JlYXRlc3QgZnJlcXVlbmN5LiBBbHNvLCBsaWtlIHRoZSBmZW1hbGVzLCB3ZSBzZWUgdGhhdCBtYWxlcyBleHBlcmllbmNlIHRoZSBmcmFjdHVyZXMgdG8gdGhlIGxvd2VyIGFuZCB1cHBlciB0cnVuayBtb3N0IGZyZXF1ZW50bHkgKGFmdGVyIHRoZSBoZWFkIGluanVyaWVzKSwgYW5kIHRoZSBtYWpvciBvZiB0aGVzZSBpbmp1cmllcyBhcmUgYXQgaG9tZS4NCg0KIyMgQ29ubmVjdGlvbnMgdG8gUHJvZHVjdHM/DQpOZXh0LCBsZXQncyBzZWUgaWYgdGhlcmUgaXMgYSB0aWUgdG8gdGhlIHByb2R1Y3RzLg0KDQpgYGB7cn0NCg0KbG9jYXRpb25yZXN1bHRzIDwtIHNxbGRmKCdTRUxFQ1QgbG9jYXRpb24sIGRpYWdub3NpcywgYm9keV9wYXJ0LCBwcm9kdWN0XzEsICBzZXgsIGNvdW50KHNleCkgQVMgImZyZXF1ZW5jeSINCiAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZGZfbWFwcGVkDQogICAgICAgICAgICAgICAgICAgICAgICBXSEVSRSAoZGlhZ25vc2lzID0gIjUzIC0gQ09OVFVTSU9OUywgQUJSLiIgb3IgZGlhZ25vc2lzID0gIjU3IC0gRlJBQ1RVUkUiIG9yIGRpYWdub3NpcyA9ICI1OSAtIExBQ0VSQVRJT04iIG9yIGRpYWdub3NpcyA9ICI2MiAtIElOVEVSTkFMIElOSlVSWSIpDQogICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSBsb2NhdGlvbiwgZGlhZ25vc2lzLCBib2R5X3BhcnQsIHByb2R1Y3RfMSwgIHNleCcpDQojc3VtKGxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpICNyZXN1bHRzID0gOTk4NjgNCiAgICAjPC0gICAgZGZfZmlyZWZyZXFbb3JkZXIoLWRmX2ZpcmVmcmVxJGZyZXF1ZW5jeSksXQ0Kc29ydGVkIDwtIGxvY2F0aW9ucmVzdWx0c1tvcmRlcigtbG9jYXRpb25yZXN1bHRzJGZyZXF1ZW5jeSksXQ0KDQpoZWFkKHNvcnRlZCkNCg0KYGBgDQpUaGlzIHF1ZXJ5IHNob3dzIHRoZSBtYWpvcml0eSBvZiBwcm9kdWN0cyBhcmUgdGllZCB0byBmbG9vcnMgb3IgZmxvb3JpbmcgbWF0ZXJpYWxzLiBEb2VzIHRoaXMgbWVhbiB0aGUgcGF0aWVudCBqdXN0IGZlbGwgb24gdGhlIGZsb29yIGxlYWRpbmcgdG8gdGhlIGludGVybmFsIGluanVyaWVzIGFuZCBmcmFjdHVyZXM/IEFsc28gb2Ygbm90ZSBob21lLCBpbnRlcm5hbCBpbmp1cnksIGFuZCBoZWFkcyBhcmUgdGhlIG1vc3QgbGlrZWx5IHRvIGJlIHRoZSBjb21iaW5hdGlvbiBmb3Igb3VyIHBhdGllbnRzLiBMZXQncyBsb29rIGF0IHRoZSBzYW1lIGRhdGEgd2l0aCBqdXN0IHRoZSBmbG9vcnMuDQoNCmBgYHtyfQ0KDQpsb2NhdGlvbnJlc3VsdHMgPC0gc3FsZGYoJ1NFTEVDVCBsb2NhdGlvbiwgZGlhZ25vc2lzLCBib2R5X3BhcnQsIHByb2R1Y3RfMSwgIGNvdW50KHByb2R1Y3RfMSkgQVMgImZyZXF1ZW5jeSINCiAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZGZfbWFwcGVkDQogICAgICAgICAgICAgICAgICAgICAgICBXSEVSRSAoZGlhZ25vc2lzID0gIjUzIC0gQ09OVFVTSU9OUywgQUJSLiIgb3IgZGlhZ25vc2lzID0gIjU3IC0gRlJBQ1RVUkUiIG9yIGRpYWdub3NpcyA9ICI1OSAtIExBQ0VSQVRJT04iIG9yIGRpYWdub3NpcyA9ICI2MiAtIElOVEVSTkFMIElOSlVSWSIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFORCAocHJvZHVjdF8xID0gIjE4MDcgLSBGTE9PUlMgT1IgRkxPT1JJTkcgTUFURVJJQUxTIikNCiAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIGxvY2F0aW9uLCBkaWFnbm9zaXMsIGJvZHlfcGFydCwgcHJvZHVjdF8xJykNCnNvcnRlZCA8LSBsb2NhdGlvbnJlc3VsdHNbb3JkZXIoLWxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpLF0NCmhlYWQoc29ydGVkKQ0KDQpzdW0gPC0gc3VtKGxvY2F0aW9ucmVzdWx0cyRmcmVxdWVuY3kpDQpwZXJjZW50IDwtIHN1bS9yb3djb3VudCAqIDEwMA0KDQpwcmludCggICBzcHJpbnRmKCJUaGUgdG90YWwgZnJlcXVlbmN5IG9mIHRoZSBmbG9vci8gZmxvb3JpbmcgbWF0ZXJpYWxzIGluanVyaWVzIGlzICVpLCBvciAlLjJmJSUuIiwgc3VtLCBwZXJjZW50ICkpDQoNCmBgYA0KQXMgd2UgY29tYmluZSB0aGUgdmFyaW91cyBkYXRhIGNvbHVtbnMgdG8gb3VyIGRhdGEgZXhwbG9yYXRpb24sIHdlIGFyZSBzZWVpbmcgZGlzdGluY3QgZ3JvdXBpbmdzIG9mIHRoZSBmYWN0b3JzIHRoYXQgbWFrZSB1cCB0aGlzIGRhdGFzZXQuDQoNCg0KIyMgVGllaW5nIFRvZ2V0aGVyIHRoZSBEYXRhIEV4cGxvcmF0aW9uLg0KQXMgcHJldmlvdXNseSBvYnNlcnZlZCwgZmVtYWxlcyBhdCBob21lLCBzdWZmZXIgaW50ZXJuYWwgaW5qdXJpZXMgdG8gdGhlaXIgaGVhZHMgd2hlbiBsYW5kaW5nIG9uIHRoZSBmbG9vcnMuIExldCdzIGxvb2sgYXQgdGhlIHJldmlzZWQgZGF0YXNldCB3aGVuIHdlIGxvb2sgYXQgdGhlIG1vc3QgZnJlcXVlbnRseSBzdXN0YWluIGluanVyaWVzLg0KDQpgYGB7cn0NCmlmIChzeXN0ZW0uZmlsZShwYWNrYWdlID0gImNpcmNsZXBhY2tldFIiKSA9PSAiIikgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJjaXJjbGVwYWNrZXRSIikNCn0NCg0KbGlicmFyeShzcWxkZikNCmxpYnJhcnkoY2lyY2xlcGFja2VSKSANCiMNCmxvY2F0aW9ucmVzdWx0cyA8LSBzcWxkZignU0VMRUNUIHNleCwgbG9jYXRpb24sIGRpYWdub3NpcywgYm9keV9wYXJ0LCBjb3VudCgqKSBBUyAiZnJlcXVlbmN5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBkZl9tYXBwZWQNCiAgICAgICAgICAgICAgICAgICAgICAgIFdIRVJFIChkaWFnbm9zaXMgPSAiNTMgLSBDT05UVVNJT05TLCBBQlIuIiBvciBkaWFnbm9zaXMgPSAiNTcgLSBGUkFDVFVSRSIgb3IgZGlhZ25vc2lzID0gIjU5IC0gTEFDRVJBVElPTiIgb3IgZGlhZ25vc2lzID0gIjYyIC0gSU5URVJOQUwgSU5KVVJZIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgc2V4LCBsb2NhdGlvbiwgZGlhZ25vc2lzLCBib2R5X3BhcnQNCiAgICAgICAgICAgICAgICAgICAgICAgIE9SREVSIEJZIGZyZXF1ZW5jeSBERVNDJykNCg0Kcm93Y3QgPC0gbnJvdyhsb2NhdGlvbnJlc3VsdHMpDQoNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgcm9vdD1yZXAoInJvb3QiLCBsb2NhdGlvbnJvd3MpLA0KICBncm91cD1jKHJlcChsb2NhdGlvbnJlc3VsdHMkc2V4LHJvd2N0KSwsICksIA0KICBzdWJncm91cD0gIHJlcChsb2NhdGlvbnJlc3VsdHMkbG9jYXRpb24scm93Y3QpLA0KICBzdWJzdWJncm91cD1yZXAocmVwKGxvY2F0aW9ucmVzdWx0cyRkaWFnbm9zaXMscm93Y3QpKSwNCiAgdmFsdWU9c2FtcGxlKHNlcSgxOjE1KSwgKQ0KKQ0KDQoNCg0KDQoNCmBgYA0KDQoNCg0KIyMgQm9keSBQYXJ0IDIsIFByb2R1Y3RzIDIsIGFuZCBQcm9kdWN0IDMNCkknbSByZWx1Y3RhbnQgdG8gZGVsdmUgdG9vIGRlZXBseSBpbnRvIHRoZXNlIGNvbHVtbnMuIEZpcnN0LCB0aGVzZSAzIGNvbHVtbnMgYXJlIG1vc3RseSBibGFuaywgc28gd2hlcmUgdGhlcmUgaXMgZGF0YSBpbiB0aGVzZSBjb2x1bW5zLCBpdCBzZWVtcyB0byBiZSBhIHNlY29uZGFyeSByZXN1bHQgb2YgdGhlIGJvZHlfcGFydCBhbmQgcHJvZHVjdF8xLiBJbiBvdGhlciB3b3JkcywgaWYgd2UgY29tZSB1cCB3aXRoIGEgd2F5IHRvIHByZXZlbnQgaW5qdXJ5IHRvIGJvZHlfcGFydCB3aXRoIHByb2R1Y3RfMSwgdGhlbiB3ZSBjYW4gcHJldmVudCBwcm9ibGVtcyB3aXRoIGJvZHlfcGFydF8yIGFuZCBwcm9kdWN0XzIgYW5kIHByb2R1Y3RfMy4NCg0KDQojIEFkZGl0aW9uIEZlYXR1cmUgRW5naW5lZXJpbmcNCg0KYGBge3J9DQpsaWJyYXJ5KHdhZmZsZSkNCg0KcmVzdWx0cyA8LSBzcWxkZignU0VMRUNUIGFnZSwgc2V4LCBjb3VudChzZXgpIEFTICJmcmVxdWVuY3kiDQogICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGRmX21hcHBlZA0KICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgYWdlLCBzZXgnKQ0KDQpnZ3Bsb3QoZGF0YTUpICsgDQpnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeD1kaWFnbm9zaXMsIHk9ZnJlcXVlbmN5LCBjb2xvcj1yYWNlLCBhbHBoYT1yYWNlLCBzaGFwZT1zZXgpKSArIA0KICAgIGxhYnModGl0bGUgPSAiRnJlcXVlbmN5IG9mIEluanVyeSBCeSBEaWFnbm9zaXMsIEdlbmRlciwgJiByYWNlIDwxMDAiLCB4PSJEaWFnbm9zaXMiLCB5PSJDb3VudCIpDQpgYGANCg0KIyBEYXRhIE1vZGVsaW5nIHdpdGggSGllYXJjaGljYWwgTW9kZWwNCg0KYGBge3J9DQoNCmBgYA0KDQojIFVzaW5nIENoYXRHUFQgZm9yIEFuYWx5c2lzIG9mIHRoZSBOYXJyYXRpdmUgRGF0YQ0KDQpgYGB7cn0NCg0KYGBgDQoNCg==